diff --git a/packages/core/src/connectors/github/constant.ts b/packages/core/src/connectors/github/constant.ts new file mode 100644 index 000000000..3c6b8f374 --- /dev/null +++ b/packages/core/src/connectors/github/constant.ts @@ -0,0 +1,2 @@ +export const authorizationEndpoint = 'https://github.com/login/oauth/authorize'; +export const scope = 'read:user'; diff --git a/packages/core/src/connectors/github/index.test.ts b/packages/core/src/connectors/github/index.test.ts new file mode 100644 index 000000000..49015defe --- /dev/null +++ b/packages/core/src/connectors/github/index.test.ts @@ -0,0 +1,17 @@ +import { getAuthorizeUri } from '.'; +import { getConnectorConfig } from '../utilities'; +import { authorizationEndpoint } from './constant'; + +jest.mock('../utilities'); + +describe('getAuthorizeUri', () => { + it('should get a valid uri by redirectUri and state', async () => { + (getConnectorConfig as jest.MockedFunction).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` + ); + }); +}); diff --git a/packages/core/src/connectors/github/index.ts b/packages/core/src/connectors/github/index.ts new file mode 100644 index 000000000..3cec1f966 --- /dev/null +++ b/packages/core/src/connectors/github/index.ts @@ -0,0 +1,38 @@ +import { ConnectorType } from '@logto/schemas'; +import { stringify } from 'query-string'; +import { z } from 'zod'; + +import { ConnectorMetadata, GetAuthorizeUri } from '../types'; +import { getConnectorConfig } from '../utilities'; +import { authorizationEndpoint, scope } from './constant'; + +export const metadata: ConnectorMetadata = { + id: 'github', + type: ConnectorType.Social, + name: { + en: 'Sign In with GitHub', + zh_CN: 'GitHub登录', + }, + logo: './logo.png', + description: { + en: 'Sign In with GitHub', + zh_CN: 'GitHub登录', + }, +}; + +const githubConfigGuard = z.object({ + clientId: z.string(), + clientSecret: z.string(), +}); + +type GithubConfig = z.infer; + +export const getAuthorizeUri: GetAuthorizeUri = async (redirectUri, state) => { + const config = await getConnectorConfig(metadata.id, metadata.type); + return `${authorizationEndpoint}?${stringify({ + client_id: config.clientId, + redirect_uri: redirectUri, + state, + scope, // Only support fixed scope for v1. + })}`; +}; diff --git a/packages/core/src/connectors/types.ts b/packages/core/src/connectors/types.ts index b0ae9d3f3..7a827993a 100644 --- a/packages/core/src/connectors/types.ts +++ b/packages/core/src/connectors/types.ts @@ -10,11 +10,20 @@ export interface ConnectorMetadata { } // The name `Connector` is used for database, use `ConnectorInstance` to avoid confusing. -export interface ConnectorInstance { +export type ConnectorInstance = EmailConector | SocialConector; + +export interface BaseConnector { metadata: ConnectorMetadata; +} + +export interface EmailConector extends BaseConnector { sendMessage: EmailSendMessageFunction; } +export interface SocialConector extends BaseConnector { + getAuthorizeUri: GetAuthorizeUri; +} + export interface EmailMessageTypes { SignIn: { code: string; @@ -41,3 +50,5 @@ export class ConnectorConfigError extends ConnectorError {} export type ValidateConfig = ( config: T ) => Promise; + +export type GetAuthorizeUri = (redirectUri: string, state: string) => Promise;