Skip to content

Commit

Permalink
feat: review fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
porcellus committed Aug 1, 2024
1 parent aac74df commit 6bab7f5
Show file tree
Hide file tree
Showing 36 changed files with 521 additions and 377 deletions.
1 change: 0 additions & 1 deletion lib/build/querier.d.ts

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

11 changes: 5 additions & 6 deletions lib/build/querier.js

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

8 changes: 5 additions & 3 deletions lib/build/recipe/oauth2provider/api/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ async function authGET(apiImplementation, options, userContext) {
try {
session = await session_1.default.getSession(options.req, options.res, { sessionRequired: false }, userContext);
} catch (_a) {
// TODO: explain
// ignore
// We ignore this here since the authGET is called from the auth endpoint which is used in full-page redirections
// Returning a 401 would break the sign-in flow.
// In theory we could serve some JS that handles refreshing and retrying, but this is not implemented.
// What we do is that the auth endpoint will redirect to the login page, and the login page handles refreshing and
// redirect to the auth endpoint again. This is not optimal, but it works for now.
}
let response = await apiImplementation.authGET({
options,
Expand All @@ -44,7 +47,6 @@ async function authGET(apiImplementation, options, userContext) {
userContext,
});
if ("redirectTo" in response) {
// TODO:
if (response.setCookie) {
const cookieStr = set_cookie_parser_1.default.splitCookiesString(response.setCookie);
const cookies = set_cookie_parser_1.default.parse(cookieStr);
Expand Down
20 changes: 5 additions & 15 deletions lib/build/recipe/oauth2provider/api/implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,6 @@ function getAPIImplementation() {
userContext,
});
},
loginPOST: async ({ loginChallenge, accept, options, session, userContext }) => {
const res = accept
? await options.recipeImplementation.acceptLoginRequest({
challenge: loginChallenge,
subject: session.getUserId(),
userContext,
})
: await options.recipeImplementation.rejectLoginRequest({
challenge: loginChallenge,
error: { error: "access_denied", errorDescription: "The resource owner denied the request" },
userContext,
});
return { redirectTo: res.redirectTo };
},
authGET: async ({ options, params, cookie, session, userContext }) => {
const response = await options.recipeImplementation.authorization({
params,
Expand All @@ -62,7 +48,10 @@ function getAPIImplementation() {
});
},
tokenPOST: async (input) => {
return input.options.recipeImplementation.token({ body: input.body, userContext: input.userContext });
return input.options.recipeImplementation.tokenExchange({
body: input.body,
userContext: input.userContext,
});
},
loginInfoGET: async ({ loginChallenge, options, userContext }) => {
const { client } = await options.recipeImplementation.getLoginRequest({
Expand All @@ -76,6 +65,7 @@ function getAPIImplementation() {
tosUri: client.tosUri,
policyUri: client.policyUri,
logoUri: client.logoUri,
clientUri: client.clientUri,
metadata: client.metadata,
},
};
Expand Down
86 changes: 30 additions & 56 deletions lib/build/recipe/oauth2provider/api/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,66 +21,40 @@ var __importDefault =
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../../../utils");
const session_1 = __importDefault(require("../../session"));
// TODO: separate post and get?
const error_1 = __importDefault(require("../../../error"));
async function login(apiImplementation, options, userContext) {
var _a;
if (utils_1.normaliseHttpMethod(options.req.getMethod()) === "post") {
if (apiImplementation.loginPOST === undefined) {
return false;
}
const session = await session_1.default.getSession(
options.req,
options.res,
{ sessionRequired: true },
userContext
);
const reqBody = await options.req.getJSONBody();
let response = await apiImplementation.loginPOST({
options,
accept: reqBody.accept,
loginChallenge: reqBody.loginChallenge,
session,
userContext,
if (apiImplementation.loginGET === undefined) {
return false;
}
let session;
try {
session = await session_1.default.getSession(options.req, options.res, { sessionRequired: false }, userContext);
} catch (_b) {
// We can handle this as if the session is not present, because then we redirect to the frontend,
// which should handle the validation error
session = undefined;
}
const loginChallenge =
(_a = options.req.getKeyValueFromQuery("login_challenge")) !== null && _a !== void 0
? _a
: options.req.getKeyValueFromQuery("loginChallenge");
if (loginChallenge === undefined) {
throw new error_1.default({
type: error_1.default.BAD_INPUT_ERROR,
message: "Missing input param: loginChallenge",
});
if ("status" in response) {
utils_1.send200Response(options.res, response);
} else {
options.res.original.redirect(response.redirectTo);
}
}
let response = await apiImplementation.loginGET({
options,
loginChallenge,
session,
userContext,
});
if ("status" in response) {
utils_1.send200Response(options.res, response);
} else {
if (apiImplementation.loginGET === undefined) {
return false;
}
let session;
try {
session = await session_1.default.getSession(
options.req,
options.res,
{ sessionRequired: false },
userContext
);
} catch (_b) {
// TODO: Claim validation failure
}
// TODO: take only one
const loginChallenge =
(_a = options.req.getKeyValueFromQuery("login_challenge")) !== null && _a !== void 0
? _a
: options.req.getKeyValueFromQuery("loginChallenge");
if (loginChallenge === undefined) {
throw new Error("TODO");
}
let response = await apiImplementation.loginGET({
options,
loginChallenge,
session,
userContext,
});
if ("status" in response) {
utils_1.send200Response(options.res, response);
} else {
options.res.original.redirect(response.redirectTo);
}
options.res.original.redirect(response.redirectTo);
}
return true;
}
Expand Down
11 changes: 10 additions & 1 deletion lib/build/recipe/oauth2provider/api/loginInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../../../utils");
const error_1 = __importDefault(require("../../../error"));
async function loginInfoGET(apiImplementation, options, userContext) {
var _a;
if (apiImplementation.loginInfoGET === undefined) {
Expand All @@ -25,7 +31,10 @@ async function loginInfoGET(apiImplementation, options, userContext) {
? _a
: options.req.getKeyValueFromQuery("loginChallenge");
if (loginChallenge === undefined) {
throw new Error("TODO");
throw new error_1.default({
type: error_1.default.BAD_INPUT_ERROR,
message: "Missing input param: loginChallenge",
});
}
let response = await apiImplementation.loginInfoGET({
options,
Expand Down
6 changes: 5 additions & 1 deletion lib/build/recipe/oauth2provider/api/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ async function tokenPOST(apiImplementation, options, userContext) {
body: await options.req.getFormData(),
userContext,
});
utils_1.send200Response(options.res, response);
if ("statusCode" in response && response.statusCode !== 200) {
utils_1.sendNon200Response(options.res, response.statusCode, response);
} else {
utils_1.send200Response(options.res, response);
}
return true;
}
exports.default = tokenPOST;
14 changes: 7 additions & 7 deletions lib/build/recipe/oauth2provider/api/userInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ async function userInfoGET(apiImplementation, tenantId, options, userContext) {
}
const authHeader = options.req.getHeaderValue("authorization") || options.req.getHeaderValue("Authorization");
if (authHeader === undefined || !authHeader.startsWith("Bearer ")) {
// TODO: Returning a 400 instead of a 401 to prevent a potential refresh loop in the client SDK.
// When addressing this TODO, review other response codes in this function as well.
utils_1.sendNon200ResponseWithMessage(options.res, "Missing or invalid Authorization header", 400);
utils_1.sendNon200ResponseWithMessage(options.res, "Missing or invalid Authorization header", 401);
return true;
}
const accessToken = authHeader.replace(/^Bearer /, "").trim();
Expand All @@ -40,13 +38,13 @@ async function userInfoGET(apiImplementation, tenantId, options, userContext) {
payload,
} = await recipe_1.default.getInstanceOrThrowError().recipeInterfaceImpl.validateOAuth2AccessToken({
token: accessToken,
// TODO: expectedAudience?
userContext,
});
accessTokenPayload = payload;
} catch (error) {
options.res.setHeader("WWW-Authenticate", 'Bearer error="invalid_token"', false);
utils_1.sendNon200ResponseWithMessage(options.res, "Invalid or expired OAuth2 access token", 400);
options.res.setHeader("Access-Control-Expose-Headers", "WWW-Authenticate", true);
utils_1.sendNon200ResponseWithMessage(options.res, "Invalid or expired OAuth2 access token", 401);
return true;
}
if (
Expand All @@ -56,17 +54,19 @@ async function userInfoGET(apiImplementation, tenantId, options, userContext) {
!Array.isArray(accessTokenPayload.scp)
) {
options.res.setHeader("WWW-Authenticate", 'Bearer error="invalid_token"', false);
utils_1.sendNon200ResponseWithMessage(options.res, "Malformed access token payload", 400);
options.res.setHeader("Access-Control-Expose-Headers", "WWW-Authenticate", true);
utils_1.sendNon200ResponseWithMessage(options.res, "Malformed access token payload", 401);
return true;
}
const userId = accessTokenPayload.sub;
const user = await __1.getUser(userId, userContext);
if (user === undefined) {
options.res.setHeader("WWW-Authenticate", 'Bearer error="invalid_token"', false);
options.res.setHeader("Access-Control-Expose-Headers", "WWW-Authenticate", true);
utils_1.sendNon200ResponseWithMessage(
options.res,
"Couldn't find any user associated with the access token",
400
401
);
return true;
}
Expand Down
33 changes: 17 additions & 16 deletions lib/build/recipe/oauth2provider/api/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,26 @@ const set_cookie_parser_1 = __importDefault(require("set-cookie-parser"));
// API implementation for the loginGET function.
// Extracted for use in both apiImplementation and handleInternalRedirects.
async function loginGET({ recipeImplementation, loginChallenge, session, setCookie, userContext }) {
var _a, _b;
var _a;
const request = await recipeImplementation.getLoginRequest({
challenge: loginChallenge,
userContext,
});
const queryParams = new URLSearchParams({
loginChallenge,
});
if ((_a = request.oidcContext) === null || _a === void 0 ? void 0 : _a.login_hint) {
queryParams.set("hint", request.oidcContext.login_hint);
}
if (request.skip) {
const accept = await recipeImplementation.acceptLoginRequest({
challenge: loginChallenge,
identityProviderSessionId: session === null || session === void 0 ? void 0 : session.getHandle(),
subject: request.subject,
userContext,
});
return { redirectTo: accept.redirectTo, setCookie };
} else if (session) {
if (session.getUserId() !== request.subject) {
// TODO?
}
} else if (session && (!request.subject || session.getUserId() === request.subject)) {
const accept = await recipeImplementation.acceptLoginRequest({
challenge: loginChallenge,
subject: session.getUserId(),
Expand All @@ -45,15 +49,7 @@ async function loginGET({ recipeImplementation, loginChallenge, session, setCook
.getAsStringDangerous();
const websiteBasePath = appInfo.websiteBasePath.getAsStringDangerous();
return {
redirectTo:
websiteDomain +
websiteBasePath +
`?hint=${
(_b = (_a = request.oidcContext) === null || _a === void 0 ? void 0 : _a.login_hint) !== null &&
_b !== void 0
? _b
: ""
}&loginChallenge=${loginChallenge}`,
redirectTo: websiteDomain + websiteBasePath + `?${queryParams.toString()}`,
setCookie,
};
}
Expand Down Expand Up @@ -90,11 +86,16 @@ function mergeSetCookieHeaders(setCookie1, setCookie2) {
function isInternalRedirect(redirectTo) {
const { apiDomain, apiBasePath } = supertokens_1.default.getInstanceOrThrowError().appInfo;
const basePath = `${apiDomain.getAsStringDangerous()}${apiBasePath.getAsStringDangerous()}`;
return [constants_1.LOGIN_PATH, constants_1.AUTH_PATH].some((path) => redirectTo.startsWith(`${basePath}${path}`));
return [
constants_1.LOGIN_PATH,
constants_1.AUTH_PATH,
constants_1.LOGIN_PATH.replace("oauth", "oauth2"),
constants_1.AUTH_PATH.replace("oauth", "oauth2"),
].some((path) => redirectTo.startsWith(`${basePath}${path}`));
}
// In the OAuth2 flow, we do several internal redirects. These redirects don't require a frontend-to-api-server round trip.
// If an internal redirect is identified, it's handled directly by this function.
// Currently, we only need to handle redirects to /oauth2provider/login and /oauth2provider/auth endpoints.
// Currently, we only need to handle redirects to /oauth/login and /oauth/auth endpoints.
async function handleInternalRedirects({ response, recipeImplementation, session, cookie = "", userContext }) {
var _a;
if (!isInternalRedirect(response.redirectTo)) {
Expand Down
12 changes: 6 additions & 6 deletions lib/build/recipe/oauth2provider/constants.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @ts-nocheck
export declare const OAUTH2_BASE_PATH = "/oauth2provider/";
export declare const LOGIN_PATH = "/oauth2provider/login";
export declare const AUTH_PATH = "/oauth2provider/auth";
export declare const TOKEN_PATH = "/oauth2provider/token";
export declare const LOGIN_INFO_PATH = "/oauth2provider/login/info";
export declare const USER_INFO_PATH = "/oauth2provider/userinfo";
export declare const OAUTH2_BASE_PATH = "/oauth/";
export declare const LOGIN_PATH = "/oauth/login";
export declare const AUTH_PATH = "/oauth/auth";
export declare const TOKEN_PATH = "/oauth/token";
export declare const LOGIN_INFO_PATH = "/oauth/login/info";
export declare const USER_INFO_PATH = "/oauth/userinfo";
12 changes: 6 additions & 6 deletions lib/build/recipe/oauth2provider/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.USER_INFO_PATH = exports.LOGIN_INFO_PATH = exports.TOKEN_PATH = exports.AUTH_PATH = exports.LOGIN_PATH = exports.OAUTH2_BASE_PATH = void 0;
exports.OAUTH2_BASE_PATH = "/oauth2provider/";
exports.LOGIN_PATH = "/oauth2provider/login";
exports.AUTH_PATH = "/oauth2provider/auth";
exports.TOKEN_PATH = "/oauth2provider/token";
exports.LOGIN_INFO_PATH = "/oauth2provider/login/info";
exports.USER_INFO_PATH = "/oauth2provider/userinfo";
exports.OAUTH2_BASE_PATH = "/oauth/";
exports.LOGIN_PATH = "/oauth/login";
exports.AUTH_PATH = "/oauth/auth";
exports.TOKEN_PATH = "/oauth/token";
exports.LOGIN_INFO_PATH = "/oauth/login/info";
exports.USER_INFO_PATH = "/oauth/userinfo";
Loading

0 comments on commit 6bab7f5

Please sign in to comment.