0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-27 21:39:16 -05:00
logto/packages/connector-github/src/index.ts
Darcy Ye 96796203dd
feat(native-connectors): pass random state to native connector sdk (#922)
* feat(native-connectors): pass random state to native connector SDK

* refactor(connector-*): update type GetAuthorizationUri

* refactor(core): fix input params order
2022-05-25 14:23:53 +08:00

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;
}
};
}