feat: Improve authentication adapter interface to support multi-factor authentication (MFA), authentication challenges, and provide a more powerful interface for writing custom authentication adapters (#8156)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable indent */
|
||||
import {
|
||||
GraphQLID,
|
||||
GraphQLObjectType,
|
||||
@@ -140,11 +141,7 @@ const load = (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLCla
|
||||
...fields,
|
||||
[field]: {
|
||||
description: `This is the object ${field}.`,
|
||||
type:
|
||||
(className === '_User' && (field === 'username' || field === 'password')) ||
|
||||
parseClass.fields[field].required
|
||||
? new GraphQLNonNull(type)
|
||||
: type,
|
||||
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
@@ -352,6 +349,14 @@ const load = (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLCla
|
||||
const parseObjectFields = {
|
||||
id: globalIdField(className, obj => obj.objectId),
|
||||
...defaultGraphQLTypes.PARSE_OBJECT_FIELDS,
|
||||
...(className === '_User'
|
||||
? {
|
||||
authDataResponse: {
|
||||
description: `auth provider response when triggered on signUp/logIn.`,
|
||||
type: defaultGraphQLTypes.OBJECT,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
const outputFields = () => {
|
||||
return classOutputFields.reduce((fields, field) => {
|
||||
|
||||
@@ -41,7 +41,7 @@ const load = parseGraphQLSchema => {
|
||||
req: { config, auth, info },
|
||||
});
|
||||
|
||||
const { sessionToken, objectId } = await objectsMutations.createObject(
|
||||
const { sessionToken, objectId, authDataResponse } = await objectsMutations.createObject(
|
||||
'_User',
|
||||
parseFields,
|
||||
config,
|
||||
@@ -50,9 +50,15 @@ const load = parseGraphQLSchema => {
|
||||
);
|
||||
|
||||
context.info.sessionToken = sessionToken;
|
||||
|
||||
const viewer = await getUserFromSessionToken(
|
||||
context,
|
||||
mutationInfo,
|
||||
'viewer.user.',
|
||||
objectId
|
||||
);
|
||||
if (authDataResponse && viewer.user) viewer.user.authDataResponse = authDataResponse;
|
||||
return {
|
||||
viewer: await getUserFromSessionToken(context, mutationInfo, 'viewer.user.', objectId),
|
||||
viewer,
|
||||
};
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
@@ -111,7 +117,7 @@ const load = parseGraphQLSchema => {
|
||||
req: { config, auth, info },
|
||||
});
|
||||
|
||||
const { sessionToken, objectId } = await objectsMutations.createObject(
|
||||
const { sessionToken, objectId, authDataResponse } = await objectsMutations.createObject(
|
||||
'_User',
|
||||
{ ...parseFields, authData },
|
||||
config,
|
||||
@@ -120,9 +126,15 @@ const load = parseGraphQLSchema => {
|
||||
);
|
||||
|
||||
context.info.sessionToken = sessionToken;
|
||||
|
||||
const viewer = await getUserFromSessionToken(
|
||||
context,
|
||||
mutationInfo,
|
||||
'viewer.user.',
|
||||
objectId
|
||||
);
|
||||
if (authDataResponse && viewer.user) viewer.user.authDataResponse = authDataResponse;
|
||||
return {
|
||||
viewer: await getUserFromSessionToken(context, mutationInfo, 'viewer.user.', objectId),
|
||||
viewer,
|
||||
};
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
@@ -146,6 +158,10 @@ const load = parseGraphQLSchema => {
|
||||
description: 'This is the password used to log in the user.',
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
},
|
||||
authData: {
|
||||
description: 'Auth data payload, needed if some required auth adapters are configured.',
|
||||
type: OBJECT,
|
||||
},
|
||||
},
|
||||
outputFields: {
|
||||
viewer: {
|
||||
@@ -155,14 +171,15 @@ const load = parseGraphQLSchema => {
|
||||
},
|
||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||
try {
|
||||
const { username, password } = deepcopy(args);
|
||||
const { username, password, authData } = deepcopy(args);
|
||||
const { config, auth, info } = context;
|
||||
|
||||
const { sessionToken, objectId } = (
|
||||
const { sessionToken, objectId, authDataResponse } = (
|
||||
await usersRouter.handleLogIn({
|
||||
body: {
|
||||
username,
|
||||
password,
|
||||
authData,
|
||||
},
|
||||
query: {},
|
||||
config,
|
||||
@@ -173,8 +190,15 @@ const load = parseGraphQLSchema => {
|
||||
|
||||
context.info.sessionToken = sessionToken;
|
||||
|
||||
const viewer = await getUserFromSessionToken(
|
||||
context,
|
||||
mutationInfo,
|
||||
'viewer.user.',
|
||||
objectId
|
||||
);
|
||||
if (authDataResponse && viewer.user) viewer.user.authDataResponse = authDataResponse;
|
||||
return {
|
||||
viewer: await getUserFromSessionToken(context, mutationInfo, 'viewer.user.', objectId),
|
||||
viewer,
|
||||
};
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
@@ -355,6 +379,57 @@ const load = parseGraphQLSchema => {
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
const challengeMutation = mutationWithClientMutationId({
|
||||
name: 'Challenge',
|
||||
description:
|
||||
'The challenge mutation can be used to initiate an authentication challenge when an auth adapter needs it.',
|
||||
inputFields: {
|
||||
username: {
|
||||
description: 'This is the username used to log in the user.',
|
||||
type: GraphQLString,
|
||||
},
|
||||
password: {
|
||||
description: 'This is the password used to log in the user.',
|
||||
type: GraphQLString,
|
||||
},
|
||||
authData: {
|
||||
description:
|
||||
'Auth data allow to preidentify the user if the auth adapter needs preidentification.',
|
||||
type: OBJECT,
|
||||
},
|
||||
challengeData: {
|
||||
description:
|
||||
'Challenge data payload, can be used to post data to auth providers to auth providers if they need data for the response.',
|
||||
type: OBJECT,
|
||||
},
|
||||
},
|
||||
outputFields: {
|
||||
challengeData: {
|
||||
description: 'Challenge response from configured auth adapters.',
|
||||
type: OBJECT,
|
||||
},
|
||||
},
|
||||
mutateAndGetPayload: async (input, context) => {
|
||||
try {
|
||||
const { config, auth, info } = context;
|
||||
|
||||
const { response } = await usersRouter.handleChallenge({
|
||||
body: input,
|
||||
config,
|
||||
auth,
|
||||
info,
|
||||
});
|
||||
return response;
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
parseGraphQLSchema.addGraphQLType(challengeMutation.args.input.type.ofType, true, true);
|
||||
parseGraphQLSchema.addGraphQLType(challengeMutation.type, true, true);
|
||||
parseGraphQLSchema.addGraphQLMutation('challenge', challengeMutation, true, true);
|
||||
};
|
||||
|
||||
export { load };
|
||||
|
||||
Reference in New Issue
Block a user