2024-11-06 17:49:18 +00:00
|
|
|
import 'reflect-metadata';
|
2024-11-08 21:57:13 +00:00
|
|
|
import {Controller} from '$lib/server/api/common/types/controller';
|
|
|
|
|
import type {OAuthUser} from '$lib/server/api/common/types/oauth';
|
|
|
|
|
import {cookieExpiresAt, createSessionTokenCookie, setSessionCookie} from '$lib/server/api/common/utils/cookies';
|
|
|
|
|
import {OAuthService} from '$lib/server/api/services/oauth.service';
|
|
|
|
|
import {SessionsService} from '$lib/server/api/services/sessions.service';
|
|
|
|
|
import {github, google} from '$lib/server/auth';
|
|
|
|
|
import {OAuth2RequestError} from 'arctic';
|
|
|
|
|
import {getCookie} from 'hono/cookie';
|
2024-11-08 01:34:13 +00:00
|
|
|
|
2024-11-08 21:57:13 +00:00
|
|
|
import {inject, injectable} from 'tsyringe';
|
2024-09-16 16:07:22 +00:00
|
|
|
|
|
|
|
|
@injectable()
|
|
|
|
|
export class OAuthController extends Controller {
|
|
|
|
|
constructor(
|
2024-11-06 17:49:18 +00:00
|
|
|
@inject(SessionsService) private sessionsService: SessionsService,
|
2024-09-18 00:32:26 +00:00
|
|
|
@inject(OAuthService) private oauthService: OAuthService,
|
|
|
|
|
) {
|
2024-11-06 17:49:18 +00:00
|
|
|
super();
|
2024-09-16 16:07:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
routes() {
|
2024-09-20 01:06:54 +00:00
|
|
|
return this.controller
|
|
|
|
|
.get('/github', async (c) => {
|
|
|
|
|
try {
|
2024-11-06 17:49:18 +00:00
|
|
|
const code = c.req.query('code')?.toString() ?? null;
|
|
|
|
|
const state = c.req.query('state')?.toString() ?? null;
|
|
|
|
|
const storedState = getCookie(c).github_oauth_state ?? null;
|
2024-09-18 00:32:26 +00:00
|
|
|
|
2024-09-20 01:06:54 +00:00
|
|
|
if (!code || !state || !storedState || state !== storedState) {
|
2024-11-06 17:49:18 +00:00
|
|
|
return c.body(null, 400);
|
2024-09-20 01:06:54 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-06 17:49:18 +00:00
|
|
|
const tokens = await github.validateAuthorizationCode(code);
|
2024-09-20 01:06:54 +00:00
|
|
|
const githubUserResponse = await fetch('https://api.github.com/user', {
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: `Bearer ${tokens.accessToken}`,
|
|
|
|
|
},
|
2024-11-06 17:49:18 +00:00
|
|
|
});
|
|
|
|
|
const githubUser: GitHubUser = await githubUserResponse.json();
|
2024-09-20 01:06:54 +00:00
|
|
|
|
2024-09-21 00:25:51 +00:00
|
|
|
const oAuthUser: OAuthUser = {
|
|
|
|
|
sub: `${githubUser.id}`,
|
|
|
|
|
username: githubUser.login,
|
2024-11-06 17:49:18 +00:00
|
|
|
email: undefined,
|
|
|
|
|
};
|
2024-09-21 00:25:51 +00:00
|
|
|
|
2024-11-06 17:49:18 +00:00
|
|
|
const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'github');
|
2024-09-20 01:06:54 +00:00
|
|
|
|
2024-11-06 17:49:18 +00:00
|
|
|
const sessionToken = this.sessionsService.generateSessionToken();
|
2024-11-08 01:34:13 +00:00
|
|
|
const session = await this.sessionsService.createSession(sessionToken, userId, '', '', false, false);
|
2024-11-08 18:41:32 +00:00
|
|
|
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
|
|
|
|
setSessionCookie(c, sessionCookie);
|
2024-11-06 17:49:18 +00:00
|
|
|
return c.json({ message: 'ok' });
|
2024-09-20 01:06:54 +00:00
|
|
|
} catch (error) {
|
2024-11-06 17:49:18 +00:00
|
|
|
console.error(error);
|
2024-09-20 01:06:54 +00:00
|
|
|
// the specific error message depends on the provider
|
|
|
|
|
if (error instanceof OAuth2RequestError) {
|
|
|
|
|
// invalid code
|
2024-11-06 17:49:18 +00:00
|
|
|
return c.body(null, 400);
|
2024-09-20 01:06:54 +00:00
|
|
|
}
|
2024-11-06 17:49:18 +00:00
|
|
|
return c.body(null, 500);
|
2024-09-16 16:07:22 +00:00
|
|
|
}
|
2024-09-20 01:06:54 +00:00
|
|
|
})
|
|
|
|
|
.get('/google', async (c) => {
|
|
|
|
|
try {
|
2024-11-06 17:49:18 +00:00
|
|
|
const code = c.req.query('code')?.toString() ?? null;
|
|
|
|
|
const state = c.req.query('state')?.toString() ?? null;
|
|
|
|
|
const storedState = getCookie(c).google_oauth_state ?? null;
|
|
|
|
|
const storedCodeVerifier = getCookie(c).google_oauth_code_verifier ?? null;
|
2024-09-20 01:06:54 +00:00
|
|
|
|
|
|
|
|
if (!code || !storedState || !storedCodeVerifier || state !== storedState) {
|
2024-11-06 17:49:18 +00:00
|
|
|
return c.body(null, 400);
|
2024-09-20 01:06:54 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-06 17:49:18 +00:00
|
|
|
const tokens = await google.validateAuthorizationCode(code, storedCodeVerifier);
|
|
|
|
|
const googleUserResponse = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
|
2024-09-20 01:06:54 +00:00
|
|
|
headers: {
|
|
|
|
|
Authorization: `Bearer ${tokens.accessToken}`,
|
|
|
|
|
},
|
2024-11-06 17:49:18 +00:00
|
|
|
});
|
|
|
|
|
const googleUser: GoogleUser = await googleUserResponse.json();
|
2024-09-18 00:32:26 +00:00
|
|
|
|
2024-09-21 00:25:51 +00:00
|
|
|
const oAuthUser: OAuthUser = {
|
|
|
|
|
sub: googleUser.sub,
|
|
|
|
|
given_name: googleUser.given_name,
|
|
|
|
|
family_name: googleUser.family_name,
|
|
|
|
|
picture: googleUser.picture,
|
|
|
|
|
username: googleUser.email,
|
|
|
|
|
email: googleUser.email,
|
|
|
|
|
email_verified: googleUser.email_verified,
|
2024-11-06 17:49:18 +00:00
|
|
|
};
|
2024-09-21 00:25:51 +00:00
|
|
|
|
2024-11-06 17:49:18 +00:00
|
|
|
const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'google');
|
2024-11-08 18:41:32 +00:00
|
|
|
const sessionToken = this.sessionsService.generateSessionToken();
|
|
|
|
|
const session = await this.sessionsService.createSession(sessionToken, userId, '', '', false, false);
|
|
|
|
|
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
|
|
|
|
setSessionCookie(c, sessionCookie);
|
2024-09-20 01:06:54 +00:00
|
|
|
|
2024-11-06 17:49:18 +00:00
|
|
|
return c.json({ message: 'ok' });
|
2024-09-20 01:06:54 +00:00
|
|
|
} catch (error) {
|
2024-11-06 17:49:18 +00:00
|
|
|
console.error(error);
|
2024-09-20 01:06:54 +00:00
|
|
|
// the specific error message depends on the provider
|
|
|
|
|
if (error instanceof OAuth2RequestError) {
|
|
|
|
|
// invalid code
|
2024-11-06 17:49:18 +00:00
|
|
|
return c.body(null, 400);
|
2024-09-20 01:06:54 +00:00
|
|
|
}
|
2024-11-06 17:49:18 +00:00
|
|
|
return c.body(null, 500);
|
2024-09-18 00:32:26 +00:00
|
|
|
}
|
2024-11-06 17:49:18 +00:00
|
|
|
});
|
2024-09-16 16:07:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface GitHubUser {
|
2024-11-06 17:49:18 +00:00
|
|
|
id: number;
|
|
|
|
|
login: string;
|
2024-09-18 00:32:26 +00:00
|
|
|
}
|
2024-09-20 01:06:54 +00:00
|
|
|
|
|
|
|
|
interface GoogleUser {
|
2024-11-06 17:49:18 +00:00
|
|
|
sub: string;
|
|
|
|
|
name: string;
|
|
|
|
|
given_name: string;
|
|
|
|
|
family_name: string;
|
|
|
|
|
picture: string;
|
|
|
|
|
email: string;
|
|
|
|
|
email_verified: boolean;
|
2024-09-20 01:06:54 +00:00
|
|
|
}
|