Start working on GitHub callback
This commit is contained in:
parent
8083bcef8e
commit
576f82f029
5 changed files with 134 additions and 1 deletions
22
backend/src/graphql/github/queries/get-viewer.ts
Normal file
22
backend/src/graphql/github/queries/get-viewer.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import gql from 'graphql-tag';
|
||||
|
||||
export const getViewerQuery = gql`query GetViewer {
|
||||
viewer {
|
||||
login
|
||||
name
|
||||
avatarUrl
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export interface Viewer {
|
||||
login: string;
|
||||
name: string | null;
|
||||
avatarUrl: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface GetViewerQueryResult {
|
||||
viewer: Viewer
|
||||
}
|
|
@ -2,13 +2,23 @@ import { GraphQLClient } from 'graphql-request';
|
|||
import { requireEnv } from '../../utils';
|
||||
import type { GetUserQueryResult, User } from './queries/get-user';
|
||||
import { getUserQuery } from './queries/get-user';
|
||||
import type { GetViewerQueryResult, Viewer } from './queries/get-viewer';
|
||||
import { getViewerQuery } from './queries/get-viewer';
|
||||
|
||||
const client = new GraphQLClient('https://api.github.com/graphql');
|
||||
client.setHeader('Authorization', `bearer ${requireEnv('GITHUB_API_TOKEN')}`);
|
||||
|
||||
export async function getUser(login: string): Promise<User> {
|
||||
const { user } = await client.request<GetUserQueryResult>(getUserQuery, {
|
||||
login,
|
||||
}, {
|
||||
Authorization: `bearer ${requireEnv('GITHUB_API_TOKEN')}`,
|
||||
});
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function getViewer(accessToken: string, tokenType: string): Promise<Viewer> {
|
||||
const { viewer } = await client.request<GetViewerQueryResult>(getViewerQuery, {}, {
|
||||
Authorization: `${tokenType} ${accessToken}`,
|
||||
});
|
||||
return viewer;
|
||||
}
|
||||
|
|
96
backend/src/routes/website-api/developer/github-callback.ts
Normal file
96
backend/src/routes/website-api/developer/github-callback.ts
Normal file
|
@ -0,0 +1,96 @@
|
|||
import got from 'got';
|
||||
import urlJoin from 'url-join';
|
||||
import Developer from '../../../database/entities/developer';
|
||||
import User from '../../../database/entities/user';
|
||||
import { ParamError } from '../../../errors';
|
||||
import { getViewer } from '../../../graphql/github/sdk';
|
||||
import type SessionData from '../../../session-data';
|
||||
import type { GitHubAuthorization, MyFastifyInstance } from '../../../types';
|
||||
import {
|
||||
getSessionData, isObject, requireEnv, validateOptionalParam, validateParam,
|
||||
} from '../../../utils';
|
||||
|
||||
function getAuthorization(sessionData: SessionData, state?: string): GitHubAuthorization | null {
|
||||
if (!state) return null;
|
||||
return sessionData.gitHubAuthorizations.get(state) ?? null;
|
||||
}
|
||||
|
||||
export default function registerGitHubCallback(server: MyFastifyInstance) {
|
||||
server.get('/developer/github-callback', async (
|
||||
request,
|
||||
reply,
|
||||
) => {
|
||||
const sessionData = getSessionData(request.session);
|
||||
if (!isObject(request.query)) {
|
||||
throw server.httpErrors.badRequest('Request query is not an object');
|
||||
}
|
||||
try {
|
||||
validateOptionalParam('error', request.query.error);
|
||||
validateOptionalParam('state', request.query.state);
|
||||
} catch (error) {
|
||||
server.log.error(error);
|
||||
if (error instanceof ParamError) {
|
||||
throw server.httpErrors.badRequest(error.message);
|
||||
}
|
||||
throw server.httpErrors.internalServerError();
|
||||
}
|
||||
const authorization = getAuthorization(sessionData, request.query.state);
|
||||
if (request.query.error) {
|
||||
if (authorization && request.query.state) sessionData.gitHubAuthorizations.delete(request.query.state);
|
||||
if (request.query.error === 'access_denied') {
|
||||
await reply.redirect(urlJoin(
|
||||
'/developer/',
|
||||
authorization?.returnTo ?? '/',
|
||||
));
|
||||
}
|
||||
throw server.httpErrors.internalServerError(`Got error response: "${request.query.error}"`);
|
||||
}
|
||||
if (!request.query.state) throw server.httpErrors.badRequest('Missing state param');
|
||||
if (!authorization) throw server.httpErrors.badRequest('Authorization not found');
|
||||
try {
|
||||
validateParam('code', request.query.code);
|
||||
} catch (error) {
|
||||
server.log.error(error);
|
||||
if (error instanceof ParamError) {
|
||||
throw server.httpErrors.badRequest(error.message);
|
||||
}
|
||||
throw server.httpErrors.internalServerError();
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await got.post<{
|
||||
token_type: string;
|
||||
access_token: string;
|
||||
scope: string;
|
||||
}>('https://github.com/login/oauth/access_token', {
|
||||
searchParams: {
|
||||
client_id: requireEnv('GITHUB_CLIENT_ID'),
|
||||
client_secret: requireEnv('GITHUB_CLIENT_SECRET'),
|
||||
code: request.query.code,
|
||||
},
|
||||
responseType: 'json',
|
||||
});
|
||||
const viewer = await getViewer(response.body.access_token, response.body.token_type);
|
||||
console.log(viewer);
|
||||
let developer = await Developer.findOne({
|
||||
where: {
|
||||
gitHubId: viewer.id,
|
||||
},
|
||||
});
|
||||
if (!developer) {
|
||||
developer = new Developer();
|
||||
developer.gitHubId = viewer.id;
|
||||
}
|
||||
developer.gitHubLogin = viewer.login;
|
||||
await developer.save();
|
||||
console.log(developer);
|
||||
} catch (error) {
|
||||
server.log.error(error);
|
||||
throw server.httpErrors.internalServerError();
|
||||
}
|
||||
// TODO: Store login info in session
|
||||
// TODO: Redirect to returnTo
|
||||
|
||||
await reply.send('DONE');
|
||||
});
|
||||
}
|
|
@ -33,6 +33,9 @@ export default function registerGitHubSignIn(server: MyFastifyInstance): void {
|
|||
authorizeUrl.searchParams.set('response_type', 'code');
|
||||
authorizeUrl.searchParams.set('redirect_uri', requireEnv('GITHUB_REDIRECT_URL'));
|
||||
authorizeUrl.searchParams.set('state', state);
|
||||
sessionData.gitHubAuthorizations.set(state, {
|
||||
returnTo: request.query.return_to,
|
||||
});
|
||||
await reply.redirect(authorizeUrl.toString());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { ApolloContext, MyFastifyInstance } from '../../types';
|
|||
import { getSessionData } from '../../utils';
|
||||
import registerAllow from './allow';
|
||||
import registerDeny from './deny';
|
||||
import registerGitHubCallback from './developer/github-callback';
|
||||
import registerGitHubSignIn from './developer/github-sign-in';
|
||||
import CreateUserResolver from './resolvers/authenticate-prompt/create-user-resolver';
|
||||
import LoginResolver from './resolvers/authenticate-prompt/login-resolver';
|
||||
|
@ -38,4 +39,5 @@ export default async function registerWebsiteApi(server: MyFastifyInstance): Pro
|
|||
registerAllow(server);
|
||||
|
||||
registerGitHubSignIn(server);
|
||||
registerGitHubCallback(server);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue