0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

refactor(core): refactor oidc sso connector (#5528)

refactor oidc sso connector
This commit is contained in:
simeng-li 2024-03-20 10:20:01 +08:00 committed by GitHub
parent f638c8e6a2
commit b994f1e2ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 57 deletions

View file

@ -1,7 +1,11 @@
import { generateStandardId } from '@logto/shared/universal';
import { conditional } from '@silverhand/essentials';
import camelcaseKeys from 'camelcase-keys';
import snakecaseKeys from 'snakecase-keys';
import assertThat from '#src/utils/assert-that.js';
import { SsoConnectorError, SsoConnectorErrorCodes } from '../types/error.js';
import {
type BaseOidcConfig,
type BasicOidcConnectorConfig,
@ -13,7 +17,7 @@ import {
type CreateSingleSignOnSession,
} from '../types/session.js';
import { fetchOidcConfig, fetchToken, getIdTokenClaims } from './utils.js';
import { fetchOidcConfig, fetchToken, getIdTokenClaims, getUserInfo } from './utils.js';
/**
* OIDC connector
@ -91,8 +95,7 @@ class OidcConnector {
* @param data unknown oidc authorization response
* @param connectorSession The connector session data from the oidc provider session storage
* @returns The user info from the OIDC provider
* @remark Forked from @logto/oidc-connector
*
*/
async getUserInfo(
connectorSession: SingleSignOnConnectorSession,
@ -102,18 +105,29 @@ class OidcConnector {
const { nonce, redirectUri } = connectorSession;
// Fetch token from the OIDC provider using authorization code
const { idToken } = await fetchToken(oidcConfig, data, redirectUri);
const { idToken, accessToken } = await fetchToken(oidcConfig, data, redirectUri);
// Decode and verify the id token
const { sub, name, picture, email, email_verified, phone, phone_verified } =
await getIdTokenClaims(idToken, oidcConfig, nonce);
assertThat(
accessToken,
new SsoConnectorError(SsoConnectorErrorCodes.AuthorizationFailed, {
message: 'The access token is missing from the response.',
})
);
// Verify the id token and get the user id
const { sub: id } = await getIdTokenClaims(idToken, oidcConfig, nonce);
// Fetch user info from the userinfo endpoint
const { sub, name, picture, email, email_verified, phone, phone_verified, ...rest } =
await getUserInfo(accessToken, oidcConfig.userinfoEndpoint);
return {
id: sub,
id,
...conditional(name && { name }),
...conditional(picture && { avatar: picture }),
...conditional(email && email_verified && { email }),
...conditional(phone && phone_verified && { phone }),
...camelcaseKeys(rest),
};
}
}

View file

@ -1,60 +1,12 @@
import { SsoProviderName } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import camelcaseKeys from 'camelcase-keys';
import assertThat from '#src/utils/assert-that.js';
import { fetchToken, getUserInfo, getIdTokenClaims } from '../OidcConnector/utils.js';
import { OidcSsoConnector } from '../OidcSsoConnector/index.js';
import { type SingleSignOnFactory } from '../index.js';
import { SsoConnectorError, SsoConnectorErrorCodes } from '../types/error.js';
import { basicOidcConnectorConfigGuard } from '../types/oidc.js';
import { type ExtendedSocialUserInfo } from '../types/saml.js';
import { type SingleSignOnConnectorSession } from '../types/session.js';
import { logoBase64, logoDarkBase64 } from './consts.js';
export class OktaSsoConnector extends OidcSsoConnector {
/**
* Override the getUserInfo method from the OidcSsoConnector class
*
* @remark Okta's IdToken does not include the sufficient user claims like email_verified, phone_verified, etc. {@link https://devforum.okta.com/t/email-verified-claim/3516/2}
* This method will fetch the user info from the userinfo endpoint instead.
*/
override async getUserInfo(
connectorSession: SingleSignOnConnectorSession,
data: unknown
): Promise<ExtendedSocialUserInfo> {
const oidcConfig = await this.getOidcConfig();
const { nonce, redirectUri } = connectorSession;
// Fetch token from the OIDC provider using authorization code
const { idToken, accessToken } = await fetchToken(oidcConfig, data, redirectUri);
assertThat(
accessToken,
new SsoConnectorError(SsoConnectorErrorCodes.AuthorizationFailed, {
message: 'The access token is missing from the response.',
})
);
// Verify the id token and get the user id
const { sub: id } = await getIdTokenClaims(idToken, oidcConfig, nonce);
// Fetch user info from the userinfo endpoint
const { sub, name, picture, email, email_verified, phone, phone_verified, ...rest } =
await getUserInfo(accessToken, oidcConfig.userinfoEndpoint);
return {
id,
...conditional(name && { name }),
...conditional(picture && { avatar: picture }),
...conditional(email && email_verified && { email }),
...conditional(phone && phone_verified && { phone }),
...camelcaseKeys(rest),
};
}
}
export class OktaSsoConnector extends OidcSsoConnector {}
export const oktaSsoConnectorFactory: SingleSignOnFactory<SsoProviderName.OKTA> = {
providerName: SsoProviderName.OKTA,