0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(github): getAccessToken (#186)

* feat(github): getAccessToken

* fix: pr
This commit is contained in:
Wang Sijie 2022-01-21 15:26:14 +08:00 committed by GitHub
parent ea756752e8
commit aaa6f4dcc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 14 deletions

View file

@ -64,6 +64,7 @@
"jest": "^27.0.6",
"jest-matcher-specific-error": "^1.0.0",
"lint-staged": "^11.1.1",
"nock": "^13.2.2",
"openapi-types": "^9.1.0",
"prettier": "^2.3.2",
"ts-jest": "^27.0.5",

View file

@ -1,2 +1,3 @@
export const authorizationEndpoint = 'https://github.com/login/oauth/authorize';
export const scope = 'read:user';
export const accessTokenEndpoint = 'https://github.com/login/oauth/access_token';

View file

@ -1,17 +1,38 @@
import { getAuthorizeUri } from '.';
import nock from 'nock';
import { getAccessToken, getAuthorizationUri } from '.';
import { getConnectorConfig } from '../utilities';
import { authorizationEndpoint } from './constant';
import { accessTokenEndpoint, authorizationEndpoint } from './constant';
jest.mock('../utilities');
describe('getAuthorizeUri', () => {
beforeAll(() => {
(getConnectorConfig as jest.MockedFunction<typeof getConnectorConfig>).mockResolvedValue({
clientId: '<client-id>',
clientSecret: '<client-secret>',
});
});
describe('getAuthorizationUri', () => {
it('should get a valid uri by redirectUri and state', async () => {
(getConnectorConfig as jest.MockedFunction<typeof getConnectorConfig>).mockResolvedValue({
clientId: 'log_xx',
});
const authorizeUri = await getAuthorizeUri('http://localhost:3000/callback', 'some_state');
expect(authorizeUri).toEqual(
`${authorizationEndpoint}?client_id=log_xx&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback&scope=read%3Auser&state=some_state`
const authorizationUri = await getAuthorizationUri(
'http://localhost:3000/callback',
'some_state'
);
expect(authorizationUri).toEqual(
`${authorizationEndpoint}?client_id=%3Cclient-id%3E&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback&scope=read%3Auser&state=some_state`
);
});
});
describe('getAccessToken', () => {
it('shoud get an accessToken by exchanging with code', async () => {
nock(accessTokenEndpoint).post('').reply(200, {
access_token: 'access_token',
scope: 'scope',
token_type: 'token_type',
});
const accessToken = await getAccessToken('code');
expect(accessToken).toEqual('access_token');
});
});

View file

@ -1,10 +1,11 @@
import { ConnectorType } from '@logto/schemas';
import got from 'got';
import { stringify } from 'query-string';
import { z } from 'zod';
import { ConnectorMetadata, GetAuthorizeUri } from '../types';
import { ConnectorMetadata, GetAccessToken, GetAuthorizationUri } from '../types';
import { getConnectorConfig } from '../utilities';
import { authorizationEndpoint, scope } from './constant';
import { authorizationEndpoint, accessTokenEndpoint, scope } from './constant';
export const metadata: ConnectorMetadata = {
id: 'github',
@ -27,7 +28,7 @@ const githubConfigGuard = z.object({
type GithubConfig = z.infer<typeof githubConfigGuard>;
export const getAuthorizeUri: GetAuthorizeUri = async (redirectUri, state) => {
export const getAuthorizationUri: GetAuthorizationUri = async (redirectUri, state) => {
const config = await getConnectorConfig<GithubConfig>(metadata.id, metadata.type);
return `${authorizationEndpoint}?${stringify({
client_id: config.clientId,
@ -36,3 +37,23 @@ export const getAuthorizeUri: GetAuthorizeUri = async (redirectUri, state) => {
scope, // Only support fixed scope for v1.
})}`;
};
export const getAccessToken: GetAccessToken = async (code) => {
const { clientId: client_id, clientSecret: client_secret } =
await getConnectorConfig<GithubConfig>(metadata.id, metadata.type);
const { access_token: accessToken } = await got
.post({
url: accessTokenEndpoint,
json: {
client_id,
client_secret,
code,
},
})
.json<{
access_token: string;
scope: string;
token_type: string;
}>();
return accessToken;
};

View file

@ -21,7 +21,8 @@ export interface EmailConector extends BaseConnector {
}
export interface SocialConector extends BaseConnector {
getAuthorizeUri: GetAuthorizeUri;
getAuthorizationUri: GetAuthorizationUri;
getAccessToken: GetAccessToken;
}
export interface EmailMessageTypes {
@ -51,4 +52,6 @@ export type ValidateConfig<T extends ConnectorConfig = ConnectorConfig> = (
config: T
) => Promise<void>;
export type GetAuthorizeUri = (redirectUri: string, state: string) => Promise<string>;
export type GetAuthorizationUri = (redirectUri: string, state: string) => Promise<string>;
export type GetAccessToken = (code: string) => Promise<string>;

View file

@ -57,6 +57,7 @@ importers:
lodash.pick: ^4.4.0
module-alias: ^2.2.2
nanoid: ^3.1.23
nock: ^13.2.2
oidc-provider: ^7.10.0
openapi-types: ^9.1.0
p-retry: ^4.6.1
@ -115,6 +116,7 @@ importers:
jest: 27.4.4
jest-matcher-specific-error: 1.0.0
lint-staged: 11.2.6
nock: 13.2.2
openapi-types: 9.3.1
prettier: 2.5.1
ts-jest: 27.1.1_dc33159234d58f1c7ac35b6119da0e94
@ -10626,6 +10628,10 @@ packages:
/lodash.pick/4.4.0:
resolution: {integrity: sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=}
/lodash.set/4.3.2:
resolution: {integrity: sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=}
dev: true
/lodash.snakecase/4.1.1:
resolution: {integrity: sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=}
dev: true
@ -11297,6 +11303,18 @@ packages:
lower-case: 2.0.2
tslib: 2.3.1
/nock/13.2.2:
resolution: {integrity: sha512-PcBHuvl9i6zfaJ50A7LS55oU+nFLv8htXIhffJO+FxyfibdZ4jEvd9kTuvkrJireBFIGMZ+oUIRpMK5gU9h//g==}
engines: {node: '>= 10.13'}
dependencies:
debug: 4.3.3
json-stringify-safe: 5.0.1
lodash.set: 4.3.2
propagate: 2.0.1
transitivePeerDependencies:
- supports-color
dev: true
/node-cleanup/2.1.2:
resolution: {integrity: sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=}
dev: true
@ -13041,6 +13059,11 @@ packages:
object-assign: 4.1.1
react-is: 16.13.1
/propagate/2.0.1:
resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==}
engines: {node: '>= 8'}
dev: true
/proto-list/1.2.4:
resolution: {integrity: sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=}
dev: true