diff --git a/.circleci/forceRunCI.sh b/.circleci/forceRunCI.sh index 03f885c8a..b7e92fde3 100755 --- a/.circleci/forceRunCI.sh +++ b/.circleci/forceRunCI.sh @@ -3,9 +3,9 @@ branch=`git rev-parse --abbrev-ref HEAD` cdiCoreMap='{ "5.1": "feat/oauth-provider-base" }' cdiPluginInterfaceMap='{ "5.1": "feat/oauth-provider-base" }' -fdiNodeMap='{ "3.1": "/feat/oauth2/base" }' +fdiNodeMap='{ "3.1": "feat/oauth2/base" }' fdiWebsiteMap='{ "3.1": "master" }' -fdiAuthReactMap='{ "3.1": "/feat/oauth2/base" }' +fdiAuthReactMap='{ "3.1": "feat/oauth2/base" }' data=`jq -cn --arg branch "$branch" \ --arg cdiCoreMap "$cdiCoreMap" \ diff --git a/lib/build/recipe/oauth2provider/recipeImplementation.js b/lib/build/recipe/oauth2provider/recipeImplementation.js index e8a1e613b..0c32b164d 100644 --- a/lib/build/recipe/oauth2provider/recipeImplementation.js +++ b/lib/build/recipe/oauth2provider/recipeImplementation.js @@ -703,10 +703,15 @@ 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. */ - console.log("input", input.params); const resp = await querier.sendGetRequest( new normalisedURLPath_1.default(`/recipe/oauth/sessions/logout`), - input.params, + { + clientId: input.params.client_id, + idTokenHint: input.params.id_token_hint, + postLogoutRedirectUri: input.params.post_logout_redirect_uri, + state: input.params.state, + logoutVerifier: input.params.logout_verifier, + }, input.userContext ); if ("error" in resp) { @@ -716,9 +721,9 @@ function getRecipeInterface( errorDescription: resp.errorDescription, }; } - const redirectTo = getUpdatedRedirectTo(appInfo, resp.redirectTo); - const redirectToURL = new URL(redirectTo); - const logoutChallenge = redirectToURL.searchParams.get("logout_challenge"); + let redirectTo = getUpdatedRedirectTo(appInfo, resp.redirectTo); + const initialRedirectToURL = new URL(redirectTo); + const logoutChallenge = initialRedirectToURL.searchParams.get("logout_challenge"); // CASE 1 (See above notes) if (logoutChallenge !== null) { // Redirect to the frontend to ask for logout confirmation if there is a valid or expired supertokens session @@ -732,17 +737,18 @@ function getRecipeInterface( }; } else { // Accept the logout challenge immediately as there is no supertokens session - return await this.acceptLogoutRequest({ - challenge: logoutChallenge, - userContext: input.userContext, - }); + redirectTo = ( + await this.acceptLogoutRequest({ + challenge: logoutChallenge, + userContext: input.userContext, + }) + ).redirectTo; } } // CASE 2 or 3 (See above notes) - // TODO: add test for this // NOTE: If no post_logout_redirect_uri is provided, Hydra redirects to a fallback page. // In this case, we redirect the user to the /auth page. - if (redirectTo.endsWith("/oauth/fallbacks/logout/callback")) { + if (redirectTo.endsWith("/fallbacks/logout/callback")) { return { redirectTo: await this.getFrontendRedirectionURL({ type: "post-logout-fallback", @@ -755,21 +761,26 @@ function getRecipeInterface( acceptLogoutRequest: async function (input) { const resp = await querier.sendPutRequest( new normalisedURLPath_1.default(`/recipe/oauth/auth/requests/logout/accept`), + { challenge: input.challenge }, {}, - { logout_challenge: input.challenge }, input.userContext ); - return { - redirectTo: getUpdatedRedirectTo(appInfo, resp.redirectTo) - // NOTE: This renaming only applies to this endpoint, hence not part of the generic "getUpdatedRedirectTo" function. - .replace("/sessions/logout", "/end_session"), - }; + const redirectTo = getUpdatedRedirectTo(appInfo, resp.redirectTo); + if (redirectTo.endsWith("/fallbacks/logout/callback")) { + return { + redirectTo: await this.getFrontendRedirectionURL({ + type: "post-logout-fallback", + userContext: input.userContext, + }), + }; + } + return { redirectTo }; }, rejectLogoutRequest: async function (input) { const resp = await querier.sendPutRequest( new normalisedURLPath_1.default(`/recipe/oauth/auth/requests/logout/reject`), {}, - { logout_challenge: input.challenge }, + { challenge: input.challenge }, input.userContext ); if (resp.status != "OK") { diff --git a/lib/build/supertokens.js b/lib/build/supertokens.js index 5092d3852..4c3462392 100644 --- a/lib/build/supertokens.js +++ b/lib/build/supertokens.js @@ -431,6 +431,11 @@ class SuperTokens { if (!utils_1.isTestEnv()) { throw new Error("calling testing function in non testing env"); } + // We call reset the OAuth2Provider recipe because it is auto-initialized + // and there is no case where we want to reset the SuperTokens instance but not + // the recipes. + let OAuth2ProviderRecipe = require("./recipe/oauth2provider/recipe").default; + OAuth2ProviderRecipe.reset(); querier_1.Querier.reset(); SuperTokens.instance = undefined; } diff --git a/lib/ts/recipe/oauth2provider/recipeImplementation.ts b/lib/ts/recipe/oauth2provider/recipeImplementation.ts index 739a0f76f..9aba329a8 100644 --- a/lib/ts/recipe/oauth2provider/recipeImplementation.ts +++ b/lib/ts/recipe/oauth2provider/recipeImplementation.ts @@ -708,10 +708,15 @@ export default function getRecipeInterface( * - Redirects to the `post_logout_redirect_uri` or the default logout fallback page. */ - console.log("input", input.params); const resp = await querier.sendGetRequest( new NormalisedURLPath(`/recipe/oauth/sessions/logout`), - input.params, + { + clientId: input.params.client_id, + idTokenHint: input.params.id_token_hint, + postLogoutRedirectUri: input.params.post_logout_redirect_uri, + state: input.params.state, + logoutVerifier: input.params.logout_verifier, + }, input.userContext ); @@ -722,10 +727,10 @@ export default function getRecipeInterface( errorDescription: resp.errorDescription, }; } - const redirectTo = getUpdatedRedirectTo(appInfo, resp.redirectTo); + let redirectTo = getUpdatedRedirectTo(appInfo, resp.redirectTo); - const redirectToURL = new URL(redirectTo); - const logoutChallenge = redirectToURL.searchParams.get("logout_challenge"); + const initialRedirectToURL = new URL(redirectTo); + const logoutChallenge = initialRedirectToURL.searchParams.get("logout_challenge"); // CASE 1 (See above notes) if (logoutChallenge !== null) { @@ -740,19 +745,20 @@ export default function getRecipeInterface( }; } else { // Accept the logout challenge immediately as there is no supertokens session - return await this.acceptLogoutRequest({ - challenge: logoutChallenge, - userContext: input.userContext, - }); + redirectTo = ( + await this.acceptLogoutRequest({ + challenge: logoutChallenge, + userContext: input.userContext, + }) + ).redirectTo; } } // CASE 2 or 3 (See above notes) - // TODO: add test for this // NOTE: If no post_logout_redirect_uri is provided, Hydra redirects to a fallback page. // In this case, we redirect the user to the /auth page. - if (redirectTo.endsWith("/oauth/fallbacks/logout/callback")) { + if (redirectTo.endsWith("/fallbacks/logout/callback")) { return { redirectTo: await this.getFrontendRedirectionURL({ type: "post-logout-fallback", @@ -766,22 +772,29 @@ export default function getRecipeInterface( acceptLogoutRequest: async function (this: RecipeInterface, input) { const resp = await querier.sendPutRequest( new NormalisedURLPath(`/recipe/oauth/auth/requests/logout/accept`), + { challenge: input.challenge }, {}, - { logout_challenge: input.challenge }, input.userContext ); - return { - redirectTo: getUpdatedRedirectTo(appInfo, resp.redirectTo) - // NOTE: This renaming only applies to this endpoint, hence not part of the generic "getUpdatedRedirectTo" function. - .replace("/sessions/logout", "/end_session"), - }; + const redirectTo = getUpdatedRedirectTo(appInfo, resp.redirectTo); + + if (redirectTo.endsWith("/fallbacks/logout/callback")) { + return { + redirectTo: await this.getFrontendRedirectionURL({ + type: "post-logout-fallback", + userContext: input.userContext, + }), + }; + } + + return { redirectTo }; }, rejectLogoutRequest: async function (this: RecipeInterface, input) { const resp = await querier.sendPutRequest( new NormalisedURLPath(`/recipe/oauth/auth/requests/logout/reject`), {}, - { logout_challenge: input.challenge }, + { challenge: input.challenge }, input.userContext ); diff --git a/lib/ts/supertokens.ts b/lib/ts/supertokens.ts index 6829e9925..f330266de 100644 --- a/lib/ts/supertokens.ts +++ b/lib/ts/supertokens.ts @@ -165,6 +165,13 @@ export default class SuperTokens { if (!isTestEnv()) { throw new Error("calling testing function in non testing env"); } + + // We call reset the OAuth2Provider recipe because it is auto-initialized + // and there is no case where we want to reset the SuperTokens instance but not + // the recipes. + let OAuth2ProviderRecipe = require("./recipe/oauth2provider/recipe").default; + OAuth2ProviderRecipe.reset(); + Querier.reset(); SuperTokens.instance = undefined; }