mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
96796203dd
* feat(native-connectors): pass random state to native connector SDK * refactor(connector-*): update type GetAuthorizationUri * refactor(core): fix input params order
104 lines
2.8 KiB
TypeScript
104 lines
2.8 KiB
TypeScript
import {
|
|
ConnectorMetadata,
|
|
GetAccessToken,
|
|
GetAuthorizationUri,
|
|
ValidateConfig,
|
|
GetUserInfo,
|
|
ConnectorError,
|
|
ConnectorErrorCodes,
|
|
SocialConnector,
|
|
GetConnectorConfig,
|
|
} from '@logto/connector-types';
|
|
import { assert, conditional } from '@silverhand/essentials';
|
|
import got, { RequestError as GotRequestError } from 'got';
|
|
|
|
import {
|
|
authorizationEndpoint,
|
|
accessTokenEndpoint,
|
|
scope,
|
|
userInfoEndpoint,
|
|
defaultMetadata,
|
|
defaultTimeout,
|
|
} from './constant';
|
|
import { githubConfigGuard, AccessTokenResponse, GithubConfig, UserInfoResponse } from './types';
|
|
|
|
export default class GithubConnector implements SocialConnector {
|
|
public metadata: ConnectorMetadata = defaultMetadata;
|
|
|
|
constructor(public readonly getConfig: GetConnectorConfig<GithubConfig>) {}
|
|
|
|
public validateConfig: ValidateConfig = async (config: unknown) => {
|
|
const result = githubConfigGuard.safeParse(config);
|
|
|
|
if (!result.success) {
|
|
throw new ConnectorError(ConnectorErrorCodes.InvalidConfig, result.error.message);
|
|
}
|
|
};
|
|
|
|
public getAuthorizationUri: GetAuthorizationUri = async (state, redirectUri) => {
|
|
const config = await this.getConfig(this.metadata.id);
|
|
|
|
const queryParameters = new URLSearchParams({
|
|
client_id: config.clientId,
|
|
redirect_uri: redirectUri,
|
|
state,
|
|
scope, // Only support fixed scope for v1.
|
|
});
|
|
|
|
return `${authorizationEndpoint}?${queryParameters.toString()}`;
|
|
};
|
|
|
|
getAccessToken: GetAccessToken = async (code) => {
|
|
const { clientId: client_id, clientSecret: client_secret } = await this.getConfig(
|
|
this.metadata.id
|
|
);
|
|
|
|
const { access_token: accessToken } = await got
|
|
.post({
|
|
url: accessTokenEndpoint,
|
|
json: {
|
|
client_id,
|
|
client_secret,
|
|
code,
|
|
},
|
|
timeout: defaultTimeout,
|
|
})
|
|
.json<AccessTokenResponse>();
|
|
|
|
assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));
|
|
|
|
return { accessToken };
|
|
};
|
|
|
|
public getUserInfo: GetUserInfo = async (accessTokenObject) => {
|
|
const { accessToken } = accessTokenObject;
|
|
|
|
try {
|
|
const {
|
|
id,
|
|
avatar_url: avatar,
|
|
email,
|
|
name,
|
|
} = await got
|
|
.get(userInfoEndpoint, {
|
|
headers: {
|
|
authorization: `token ${accessToken}`,
|
|
},
|
|
timeout: defaultTimeout,
|
|
})
|
|
.json<UserInfoResponse>();
|
|
|
|
return {
|
|
id: String(id),
|
|
avatar: conditional(avatar),
|
|
email: conditional(email),
|
|
name: conditional(name),
|
|
};
|
|
} catch (error: unknown) {
|
|
if (error instanceof GotRequestError && error.response?.statusCode === 401) {
|
|
throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid);
|
|
}
|
|
throw error;
|
|
}
|
|
};
|
|
}
|