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:
parent
ea756752e8
commit
aaa6f4dcc2
6 changed files with 84 additions and 14 deletions
|
@ -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",
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue