Skip to content

Commit

Permalink
feat: make loginGET return the redirection link as a JSON response in…
Browse files Browse the repository at this point in the history
…stead
  • Loading branch information
porcellus committed Sep 25, 2024
1 parent 055b29d commit 9506071
Show file tree
Hide file tree
Showing 16 changed files with 68 additions and 47 deletions.
2 changes: 2 additions & 0 deletions lib/build/querier.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions lib/build/recipe/oauth2provider/OAuth2Client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@ export declare class OAuth2Client {
* ClientURI is a URL string of a web page providing information about the client.
*/
clientUri: string;
/**
* Array of allowed CORS origins
* StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.
*/
allowedCorsOrigins: string[];
/**
* Array of audiences
* StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.
Expand Down Expand Up @@ -167,7 +162,6 @@ export declare class OAuth2Client {
refreshTokenGrantRefreshTokenLifespan,
tokenEndpointAuthMethod,
clientUri,
allowedCorsOrigins,
audience,
grantTypes,
responseTypes,
Expand Down
2 changes: 0 additions & 2 deletions lib/build/recipe/oauth2provider/OAuth2Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class OAuth2Client {
refreshTokenGrantRefreshTokenLifespan = null,
tokenEndpointAuthMethod,
clientUri = "",
allowedCorsOrigins = [],
audience = [],
grantTypes = null,
responseTypes = null,
Expand Down Expand Up @@ -68,7 +67,6 @@ class OAuth2Client {
this.refreshTokenGrantRefreshTokenLifespan = refreshTokenGrantRefreshTokenLifespan;
this.tokenEndpointAuthMethod = tokenEndpointAuthMethod;
this.clientUri = clientUri;
this.allowedCorsOrigins = allowedCorsOrigins;
this.audience = audience;
this.grantTypes = grantTypes;
this.responseTypes = responseTypes;
Expand Down
9 changes: 8 additions & 1 deletion lib/build/recipe/oauth2provider/api/implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,21 @@ function getAPIImplementation() {
isDirectCall: true,
userContext,
});
return utils_1.handleLoginInternalRedirects({
const respAfterInternalRedirects = await utils_1.handleLoginInternalRedirects({
response,
cookie: options.req.getHeaderValue("cookie"),
recipeImplementation: options.recipeImplementation,
session,
shouldTryRefresh,
userContext,
});
if ("error" in respAfterInternalRedirects) {
return respAfterInternalRedirects;
}
return {
frontendRedirectTo: respAfterInternalRedirects.redirectTo,
setCookie: respAfterInternalRedirects.setCookie,
};
},
authGET: async ({ options, params, cookie, session, shouldTryRefresh, userContext }) => {
const response = await options.recipeImplementation.authorization({
Expand Down
6 changes: 4 additions & 2 deletions lib/build/recipe/oauth2provider/api/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ async function login(apiImplementation, options, userContext) {
shouldTryRefresh,
userContext,
});
if ("redirectTo" in response) {
if ("frontendRedirectTo" in response) {
if (response.setCookie) {
const cookieStr = set_cookie_parser_1.default.splitCookiesString(response.setCookie);
const cookies = set_cookie_parser_1.default.parse(cookieStr);
Expand All @@ -77,7 +77,9 @@ async function login(apiImplementation, options, userContext) {
);
}
}
options.res.original.redirect(response.redirectTo);
utils_1.send200Response(options.res, {
frontendRedirectTo: response.frontendRedirectTo,
});
} else if ("statusCode" in response) {
utils_1.sendNon200ResponseWithMessage(
options.res,
Expand Down
3 changes: 3 additions & 0 deletions lib/build/recipe/oauth2provider/recipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,12 @@ class Recipe extends recipeModule_1.default {
sub: accessTokenPayload.sub,
};
if (scopes.includes("email")) {
// TODO: try and get the email based on the user id of the entire user object
payload.email = user === null || user === void 0 ? void 0 : user.emails[0];
payload.email_verified = user.loginMethods.some(
(lm) => lm.hasSameEmailAs(user === null || user === void 0 ? void 0 : user.emails[0]) && lm.verified
);
payload.emails = user.emails;
}
if (scopes.includes("phoneNumber")) {
payload.phoneNumber = user === null || user === void 0 ? void 0 : user.phoneNumbers[0];
Expand All @@ -265,6 +267,7 @@ class Recipe extends recipeModule_1.default {
lm.hasSamePhoneNumberAs(user === null || user === void 0 ? void 0 : user.phoneNumbers[0]) &&
lm.verified
);
payload.phoneNumbers = user.phoneNumbers;
}
for (const fn of this.userInfoBuilders) {
payload = Object.assign(
Expand Down
15 changes: 9 additions & 6 deletions lib/build/recipe/oauth2provider/recipeImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,6 @@ function getRecipeInterface(
{
pageSize: input.pageSize,
clientName: input.clientName,
owner: input.owner,
pageToken: input.paginationToken,
},
{},
Expand Down Expand Up @@ -704,16 +703,20 @@ function getRecipeInterface(
* CASE 3: `end_session` request with a `logout_verifier` (after accepting the logout request)
* - Redirects to the `post_logout_redirect_uri` or the default logout fallback page.
*/
const resp = await querier.sendGetRequestWithResponseHeaders(
console.log("input", input.params);
const resp = await querier.sendGetRequest(
new normalisedURLPath_1.default(`/recipe/oauth/sessions/logout`),
input.params,
{},
input.userContext
);
const redirectTo = getUpdatedRedirectTo(appInfo, resp.headers.get("Location"));
if (redirectTo === undefined) {
throw new Error(resp.body);
if ("error" in resp) {
return {
statusCode: resp.statusCode,
error: resp.error,
errorDescription: resp.errorDescription,
};
}
const redirectTo = getUpdatedRedirectTo(appInfo, resp.redirectTo);
const redirectToURL = new URL(redirectTo);
const logoutChallenge = redirectToURL.searchParams.get("logout_challenge");
// CASE 1 (See above notes)
Expand Down
7 changes: 1 addition & 6 deletions lib/build/recipe/oauth2provider/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ export declare type APIInterface = {
userContext: UserContext;
}) => Promise<
| {
redirectTo: string;
frontendRedirectTo: string;
setCookie?: string;
}
| ErrorOAuth2
Expand Down Expand Up @@ -510,7 +510,6 @@ export declare type OAuth2ClientOptions = {
scope: string;
redirectUris?: string[] | null;
postLogoutRedirectUris?: string[];
allowedCorsOrigins?: string[];
authorizationCodeGrantAccessTokenLifespan?: string | null;
authorizationCodeGrantIdTokenLifespan?: string | null;
authorizationCodeGrantRefreshTokenLifespan?: string | null;
Expand Down Expand Up @@ -543,10 +542,6 @@ export declare type GetOAuth2ClientsInput = {
* The name of the clients to filter by.
*/
clientName?: string;
/**
* The owner of the clients to filter by.
*/
owner?: string;
};
export declare type CreateOAuth2ClientInput = Partial<
Omit<OAuth2ClientOptions, "createdAt" | "updatedAt" | "clientId" | "clientSecret">
Expand Down
2 changes: 2 additions & 0 deletions lib/ts/querier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ export class Querier {
);
finalURL.search = searchParams.toString();

console.log("finalURL", finalURL.toString());
// Update cache and return
let response = await doFetch(finalURL.toString(), {
method: "GET",
Expand Down Expand Up @@ -611,6 +612,7 @@ export class Querier {
Querier.hostsAliveForTesting.add(currentDomain + currentBasePath);
}

console.log("response", { url, body: await response.clone().text(), headers: response.headers });
if (response.status !== 200) {
throw response;
}
Expand Down
8 changes: 0 additions & 8 deletions lib/ts/recipe/oauth2provider/OAuth2Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,6 @@ export class OAuth2Client {
*/
clientUri: string;

/**
* Array of allowed CORS origins
* StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.
*/
allowedCorsOrigins: string[];

/**
* Array of audiences
* StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.
Expand Down Expand Up @@ -210,7 +204,6 @@ export class OAuth2Client {
refreshTokenGrantRefreshTokenLifespan = null,
tokenEndpointAuthMethod,
clientUri = "",
allowedCorsOrigins = [],
audience = [],
grantTypes = null,
responseTypes = null,
Expand Down Expand Up @@ -238,7 +231,6 @@ export class OAuth2Client {
this.refreshTokenGrantRefreshTokenLifespan = refreshTokenGrantRefreshTokenLifespan;
this.tokenEndpointAuthMethod = tokenEndpointAuthMethod;
this.clientUri = clientUri;
this.allowedCorsOrigins = allowedCorsOrigins;
this.audience = audience;
this.grantTypes = grantTypes;
this.responseTypes = responseTypes;
Expand Down
11 changes: 10 additions & 1 deletion lib/ts/recipe/oauth2provider/api/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,23 @@ export default function getAPIImplementation(): APIInterface {
isDirectCall: true,
userContext,
});
return handleLoginInternalRedirects({
const respAfterInternalRedirects = await handleLoginInternalRedirects({
response,
cookie: options.req.getHeaderValue("cookie"),
recipeImplementation: options.recipeImplementation,
session,
shouldTryRefresh,
userContext,
});

if ("error" in respAfterInternalRedirects) {
return respAfterInternalRedirects;
}

return {
frontendRedirectTo: respAfterInternalRedirects.redirectTo,
setCookie: respAfterInternalRedirects.setCookie,
};
},

authGET: async ({ options, params, cookie, session, shouldTryRefresh, userContext }) => {
Expand Down
6 changes: 4 additions & 2 deletions lib/ts/recipe/oauth2provider/api/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default async function login(
userContext,
});

if ("redirectTo" in response) {
if ("frontendRedirectTo" in response) {
if (response.setCookie) {
const cookieStr = setCookieParser.splitCookiesString(response.setCookie);
const cookies = setCookieParser.parse(cookieStr);
Expand All @@ -78,7 +78,9 @@ export default async function login(
);
}
}
options.res.original.redirect(response.redirectTo);
send200Response(options.res, {
frontendRedirectTo: response.frontendRedirectTo,
});
} else if ("statusCode" in response) {
sendNon200ResponseWithMessage(
options.res,
Expand Down
3 changes: 3 additions & 0 deletions lib/ts/recipe/oauth2provider/recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,14 +315,17 @@ export default class Recipe extends RecipeModule {
sub: accessTokenPayload.sub,
};
if (scopes.includes("email")) {
// TODO: try and get the email based on the user id of the entire user object
payload.email = user?.emails[0];
payload.email_verified = user.loginMethods.some((lm) => lm.hasSameEmailAs(user?.emails[0]) && lm.verified);
payload.emails = user.emails;
}
if (scopes.includes("phoneNumber")) {
payload.phoneNumber = user?.phoneNumbers[0];
payload.phoneNumber_verified = user.loginMethods.some(
(lm) => lm.hasSamePhoneNumberAs(user?.phoneNumbers[0]) && lm.verified
);
payload.phoneNumbers = user.phoneNumbers;
}

for (const fn of this.userInfoBuilders) {
Expand Down
15 changes: 9 additions & 6 deletions lib/ts/recipe/oauth2provider/recipeImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ export default function getRecipeInterface(
{
pageSize: input.pageSize,
clientName: input.clientName,
owner: input.owner,
pageToken: input.paginationToken,
},
{},
Expand Down Expand Up @@ -709,17 +708,21 @@ export default function getRecipeInterface(
* - Redirects to the `post_logout_redirect_uri` or the default logout fallback page.
*/

const resp = await querier.sendGetRequestWithResponseHeaders(
console.log("input", input.params);
const resp = await querier.sendGetRequest(
new NormalisedURLPath(`/recipe/oauth/sessions/logout`),
input.params,
{},
input.userContext
);

const redirectTo = getUpdatedRedirectTo(appInfo, resp.headers.get("Location")!);
if (redirectTo === undefined) {
throw new Error(resp.body);
if ("error" in resp) {
return {
statusCode: resp.statusCode,
error: resp.error,
errorDescription: resp.errorDescription,
};
}
const redirectTo = getUpdatedRedirectTo(appInfo, resp.redirectTo);

const redirectToURL = new URL(redirectTo);
const logoutChallenge = redirectToURL.searchParams.get("logout_challenge");
Expand Down
8 changes: 1 addition & 7 deletions lib/ts/recipe/oauth2provider/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ export type APIInterface = {
session?: SessionContainerInterface;
shouldTryRefresh: boolean;
userContext: UserContext;
}) => Promise<{ redirectTo: string; setCookie?: string } | ErrorOAuth2 | GeneralErrorResponse>);
}) => Promise<{ frontendRedirectTo: string; setCookie?: string } | ErrorOAuth2 | GeneralErrorResponse>);

authGET:
| undefined
Expand Down Expand Up @@ -533,7 +533,6 @@ export type OAuth2ClientOptions = {
scope: string;
redirectUris?: string[] | null;
postLogoutRedirectUris?: string[];
allowedCorsOrigins?: string[];

authorizationCodeGrantAccessTokenLifespan?: string | null;
authorizationCodeGrantIdTokenLifespan?: string | null;
Expand Down Expand Up @@ -573,11 +572,6 @@ export type GetOAuth2ClientsInput = {
* The name of the clients to filter by.
*/
clientName?: string;

/**
* The owner of the clients to filter by.
*/
owner?: string;
};

export type CreateOAuth2ClientInput = Partial<
Expand Down
12 changes: 12 additions & 0 deletions test/auth-react-server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const TOTPRaw = require("../../lib/build/recipe/totp/recipe").default;
const TOTP = require("../../recipe/totp");
const OTPAuth = require("otpauth");

const OAuth2ProviderRaw = require("../../lib/build/recipe/oauth2provider/recipe").default;
const OAuth2Provider = require("../../recipe/oauth2provider");

let {
startST,
killAllST,
Expand Down Expand Up @@ -502,6 +505,11 @@ app.post("/test/getTOTPCode", (req, res) => {
res.send(JSON.stringify({ totp: new OTPAuth.TOTP({ secret: req.body.secret, digits: 6, period: 1 }).generate() }));
});

app.post("/test/create-oauth2-client", async (req, res) => {
const { client } = await OAuth2Provider.createOAuth2Client(req.body);
res.send({ client });
});

app.get("/test/featureFlags", (req, res) => {
const available = [];

Expand All @@ -515,6 +523,7 @@ app.get("/test/featureFlags", (req, res) => {
available.push("mfa");
available.push("recipeConfig");
available.push("accountlinking-fixes"); // this is related to 19.0 release in which we fixed a bunch of issues with account linking, including changing error codes.
available.push("oauth2");

res.send({
available,
Expand Down Expand Up @@ -568,6 +577,7 @@ function initST({ passwordlessConfig } = {}) {
UserMetadataRaw.reset();
MultiFactorAuthRaw.reset();
TOTPRaw.reset();
OAuth2ProviderRaw.reset();
SuperTokensRaw.reset();

passwordlessConfig = {
Expand Down Expand Up @@ -937,6 +947,8 @@ function initST({ passwordlessConfig } = {}) {
}),
]);

recipeList.push(["oauth2", OAuth2Provider.init()]);

SuperTokens.init({
appInfo: {
appName: "SuperTokens",
Expand Down

0 comments on commit 9506071

Please sign in to comment.