Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: integrate with OAuth2 core impl #926

Merged
merged 6 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/build/combinedRemoteJWKSet.d.ts

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

18 changes: 2 additions & 16 deletions lib/build/combinedRemoteJWKSet.js

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

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.

67 changes: 3 additions & 64 deletions lib/build/querier.js

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

9 changes: 0 additions & 9 deletions lib/build/recipe/jwt/api/implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,6 @@ function getAPIImplementation() {
if (resp.validityInSeconds !== undefined) {
options.res.setHeader("Cache-Control", `max-age=${resp.validityInSeconds}, must-revalidate`, false);
}
const oauth2Provider = require("../../oauth2provider/recipe").default.getInstance();
// TODO: dirty hack until we get core support
if (oauth2Provider !== undefined) {
const oauth2JWKSRes = await fetch("http://localhost:4444/.well-known/jwks.json");
if (oauth2JWKSRes.ok) {
const oauth2RespBody = await oauth2JWKSRes.json();
resp.keys = resp.keys.concat(oauth2RespBody.keys);
}
}
return {
keys: resp.keys,
};
Expand Down
16 changes: 14 additions & 2 deletions lib/build/recipe/oauth2client/api/implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,20 @@ const session_1 = __importDefault(require("../../session"));
function getAPIInterface() {
return {
signInPOST: async function (input) {
const { options, tenantId, userContext } = input;
const providerConfig = await options.recipeImplementation.getProviderConfig({ userContext });
const { options, tenantId, userContext, clientId } = input;
let normalisedClientId = clientId;
if (normalisedClientId === undefined) {
if (options.config.providerConfigs.length > 1) {
throw new Error(
"Should never come here: clientId is undefined and there are multiple providerConfigs"
);
}
normalisedClientId = options.config.providerConfigs[0].clientId;
}
const providerConfig = await options.recipeImplementation.getProviderConfig({
clientId: normalisedClientId,
userContext,
});
let oAuthTokensToUse = {};
if ("redirectURIInfo" in input && input.redirectURIInfo !== undefined) {
oAuthTokensToUse = await options.recipeImplementation.exchangeAuthCodeForOAuthTokens({
Expand Down
7 changes: 7 additions & 0 deletions lib/build/recipe/oauth2client/api/signin.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ async function signInAPI(apiImplementation, tenantId, options, userContext) {
const bodyParams = await options.req.getJSONBody();
let redirectURIInfo;
let oAuthTokens;
if (bodyParams.clientId === undefined && options.config.providerConfigs.length > 1) {
throw new error_1.default({
type: error_1.default.BAD_INPUT_ERROR,
message: "Please provide the clientId in request body",
});
}
if (bodyParams.redirectURIInfo !== undefined) {
if (bodyParams.redirectURIInfo.redirectURI === undefined) {
throw new error_1.default({
Expand Down Expand Up @@ -59,6 +65,7 @@ async function signInAPI(apiImplementation, tenantId, options, userContext) {
}
let result = await apiImplementation.signInPOST({
tenantId,
clientId: bodyParams.clientId,
redirectURIInfo,
oAuthTokens,
options,
Expand Down
1 change: 1 addition & 0 deletions lib/build/recipe/oauth2client/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default class Wrapper {
redirectURIQueryParams: any;
pkceCodeVerifier?: string | undefined;
},
clientId?: string,
userContext?: Record<string, any>
): Promise<import("./types").OAuthTokenResponse>;
static getUserInfo(
Expand Down
19 changes: 17 additions & 2 deletions lib/build/recipe/oauth2client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,22 @@ var __importDefault =
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUserInfo = exports.exchangeAuthCodeForOAuthTokens = exports.init = void 0;
const utils_1 = require("../../utils");
const jwt_1 = require("../session/jwt");
const recipe_1 = __importDefault(require("./recipe"));
class Wrapper {
static async exchangeAuthCodeForOAuthTokens(redirectURIInfo, userContext) {
const recipeInterfaceImpl = recipe_1.default.getInstanceOrThrowError().recipeInterfaceImpl;
static async exchangeAuthCodeForOAuthTokens(redirectURIInfo, clientId, userContext) {
let normalisedClientId = clientId;
const instance = recipe_1.default.getInstanceOrThrowError();
const recipeInterfaceImpl = instance.recipeInterfaceImpl;
const normalisedUserContext = utils_1.getUserContext(userContext);
if (normalisedClientId === undefined) {
if (instance.config.providerConfigs.length > 1) {
throw new Error("clientId is required if there are more than one provider configs defined");
}
normalisedClientId = instance.config.providerConfigs[0].clientId;
}
const providerConfig = await recipeInterfaceImpl.getProviderConfig({
clientId: normalisedClientId,
userContext: normalisedUserContext,
});
return await recipeInterfaceImpl.exchangeAuthCodeForOAuthTokens({
Expand All @@ -38,7 +48,12 @@ class Wrapper {
static async getUserInfo(oAuthTokens, userContext) {
const recipeInterfaceImpl = recipe_1.default.getInstanceOrThrowError().recipeInterfaceImpl;
const normalisedUserContext = utils_1.getUserContext(userContext);
if (oAuthTokens.access_token === undefined) {
throw new Error("access_token is required to get user info");
}
const preparseJWTInfo = jwt_1.parseJWTWithoutSignatureVerification(oAuthTokens.access_token);
const providerConfig = await recipeInterfaceImpl.getProviderConfig({
clientId: preparseJWTInfo.payload.client_id,
userContext: normalisedUserContext,
});
return await recipe_1.default.getInstanceOrThrowError().recipeInterfaceImpl.getUserInfo({
Expand Down
17 changes: 10 additions & 7 deletions lib/build/recipe/oauth2client/recipeImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const __1 = require("../..");
const logger_1 = require("../../logger");
const jose_1 = require("jose");
function getRecipeImplementation(_querier, config) {
let providerConfigWithOIDCInfo = null;
let providerConfigsWithOIDCInfo = {};
return {
signIn: async function ({ userId, tenantId, userContext, oAuthTokens, rawUserInfo }) {
const user = await __1.getUser(userId, userContext);
Expand All @@ -26,11 +26,14 @@ function getRecipeImplementation(_querier, config) {
rawUserInfo,
};
},
getProviderConfig: async function () {
if (providerConfigWithOIDCInfo !== null) {
return providerConfigWithOIDCInfo;
getProviderConfig: async function ({ clientId }) {
if (providerConfigsWithOIDCInfo[clientId] !== undefined) {
return providerConfigsWithOIDCInfo[clientId];
}
const oidcInfo = await thirdpartyUtils_1.getOIDCDiscoveryInfo(config.providerConfig.oidcDiscoveryEndpoint);
const providerConfig = config.providerConfigs.find(
(providerConfig) => providerConfig.clientId === clientId
);
const oidcInfo = await thirdpartyUtils_1.getOIDCDiscoveryInfo(providerConfig.oidcDiscoveryEndpoint);
if (oidcInfo.authorization_endpoint === undefined) {
throw new Error("Failed to authorization_endpoint from the oidcDiscoveryEndpoint.");
}
Expand All @@ -43,13 +46,13 @@ function getRecipeImplementation(_querier, config) {
if (oidcInfo.jwks_uri === undefined) {
throw new Error("Failed to jwks_uri from the oidcDiscoveryEndpoint.");
}
providerConfigWithOIDCInfo = Object.assign(Object.assign({}, config.providerConfig), {
providerConfigsWithOIDCInfo[clientId] = Object.assign(Object.assign({}, providerConfig), {
authorizationEndpoint: oidcInfo.authorization_endpoint,
tokenEndpoint: oidcInfo.token_endpoint,
userInfoEndpoint: oidcInfo.userinfo_endpoint,
jwksURI: oidcInfo.jwks_uri,
});
return providerConfigWithOIDCInfo;
return providerConfigsWithOIDCInfo[clientId];
},
exchangeAuthCodeForOAuthTokens: async function ({ providerConfig, redirectURIInfo }) {
if (providerConfig.tokenEndpoint === undefined) {
Expand Down
7 changes: 4 additions & 3 deletions lib/build/recipe/oauth2client/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export declare type OAuthTokenResponse = {
token_type: string;
};
export declare type TypeInput = {
providerConfig: ProviderConfigInput;
providerConfigs: ProviderConfigInput[];
override?: {
functions?: (
originalImplementation: RecipeInterface,
Expand All @@ -50,7 +50,7 @@ export declare type TypeInput = {
};
};
export declare type TypeNormalisedInput = {
providerConfig: ProviderConfigInput;
providerConfigs: ProviderConfigInput[];
override: {
functions: (
originalImplementation: RecipeInterface,
Expand All @@ -60,7 +60,7 @@ export declare type TypeNormalisedInput = {
};
};
export declare type RecipeInterface = {
getProviderConfig(input: { userContext: UserContext }): Promise<ProviderConfigWithOIDCInfo>;
getProviderConfig(input: { clientId: string; userContext: UserContext }): Promise<ProviderConfigWithOIDCInfo>;
signIn(input: {
userId: string;
oAuthTokens: OAuthTokens;
Expand Down Expand Up @@ -116,6 +116,7 @@ export declare type APIInterface = {
signInPOST: (
input: {
tenantId: string;
clientId?: string;
options: APIOptions;
userContext: UserContext;
} & (
Expand Down
Loading
Loading