mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Adding license and starting google oauth.
This commit is contained in:
parent
9b50e0fb48
commit
cb271d377e
10 changed files with 172 additions and 54 deletions
|
|
@ -21,6 +21,8 @@ TWO_FACTOR_TIMEOUT=300000
|
|||
# OAuth
|
||||
GITHUB_CLIENT_ID=""
|
||||
GITHUB_CLIENT_SECRET=""
|
||||
GOOGLE_CLIENT_ID=""
|
||||
GOOGLE_CLIENT_SECRET=""
|
||||
|
||||
# Public
|
||||
|
||||
|
|
|
|||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License Copyright (c) 2024 Bradley Shellnut
|
||||
|
||||
Permission is hereby granted,
|
||||
free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
(including the next paragraph) shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -129,5 +129,6 @@
|
|||
"tailwindcss-animate": "^1.0.7",
|
||||
"tsyringe": "^4.8.0",
|
||||
"zod-to-json-schema": "^3.23.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
@ -19,6 +19,8 @@ const EnvSchema = z.object({
|
|||
DB_SEEDING: stringBoolean,
|
||||
GITHUB_CLIENT_ID: z.string(),
|
||||
GITHUB_CLIENT_SECRET: z.string(),
|
||||
GOOGLE_CLIENT_ID: z.string(),
|
||||
GOOGLE_CLIENT_SECRET: z.string(),
|
||||
NODE_ENV: z.string().default('development'),
|
||||
ORIGIN: z.string(),
|
||||
PUBLIC_SITE_NAME: z.string(),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import 'reflect-metadata'
|
|||
import { Controller } from '$lib/server/api/common/types/controller'
|
||||
import { LuciaService } from '$lib/server/api/services/lucia.service'
|
||||
import { OAuthService } from '$lib/server/api/services/oauth.service'
|
||||
import { github } from '$lib/server/auth'
|
||||
import { github, google } from '$lib/server/auth'
|
||||
import { OAuth2RequestError } from 'arctic'
|
||||
import { getCookie, setCookie } from 'hono/cookie'
|
||||
import { TimeSpan } from 'oslo'
|
||||
|
|
@ -18,55 +18,108 @@ export class OAuthController extends Controller {
|
|||
}
|
||||
|
||||
routes() {
|
||||
return this.controller.get('/github', async (c) => {
|
||||
try {
|
||||
const code = c.req.query('code')?.toString() ?? null
|
||||
const state = c.req.query('state')?.toString() ?? null
|
||||
const storedState = getCookie(c).github_oauth_state ?? null
|
||||
return this.controller
|
||||
.get('/github', async (c) => {
|
||||
try {
|
||||
const code = c.req.query('code')?.toString() ?? null
|
||||
const state = c.req.query('state')?.toString() ?? null
|
||||
const storedState = getCookie(c).github_oauth_state ?? null
|
||||
|
||||
console.log('code', code, 'state', state, 'storedState', storedState)
|
||||
console.log('code', code, 'state', state, 'storedState', storedState)
|
||||
|
||||
if (!code || !state || !storedState || state !== storedState) {
|
||||
return c.body(null, 400)
|
||||
if (!code || !state || !storedState || state !== storedState) {
|
||||
return c.body(null, 400)
|
||||
}
|
||||
|
||||
const tokens = await github.validateAuthorizationCode(code)
|
||||
const githubUserResponse = await fetch('https://api.github.com/user', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
})
|
||||
const githubUser: GitHubUser = await githubUserResponse.json()
|
||||
|
||||
const userId = await this.oauthService.handleOAuthUser(githubUser.id, githubUser.login, 'github')
|
||||
|
||||
const session = await this.luciaService.lucia.createSession(userId, {})
|
||||
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id)
|
||||
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge:
|
||||
sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
|
||||
? sessionCookie.attributes.maxAge
|
||||
: new TimeSpan(2, 'w').seconds(),
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
})
|
||||
|
||||
return c.json({ message: 'ok' })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
// the specific error message depends on the provider
|
||||
if (error instanceof OAuth2RequestError) {
|
||||
// invalid code
|
||||
return c.body(null, 400)
|
||||
}
|
||||
return c.body(null, 500)
|
||||
}
|
||||
})
|
||||
.get('/google', async (c) => {
|
||||
try {
|
||||
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
|
||||
|
||||
const tokens = await github.validateAuthorizationCode(code)
|
||||
const githubUserResponse = await fetch('https://api.github.com/user', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
})
|
||||
const githubUser: GitHubUser = await githubUserResponse.json()
|
||||
console.log('code', code, 'state', state, 'storedState', storedState)
|
||||
|
||||
const userId = await this.oauthService.handleOAuthUser(githubUser.id, githubUser.login, 'github')
|
||||
if (!code || !storedState || !storedCodeVerifier || state !== storedState) {
|
||||
return c.body(null, 400)
|
||||
}
|
||||
|
||||
const session = await this.luciaService.lucia.createSession(userId, {})
|
||||
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id)
|
||||
const tokens = await google.validateAuthorizationCode(code, storedCodeVerifier)
|
||||
console.log('tokens', tokens)
|
||||
const googleUserResponse = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
})
|
||||
console.log('googleUserResponse', googleUserResponse)
|
||||
const googleUser: GoogleUser = await googleUserResponse.json()
|
||||
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge:
|
||||
sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
|
||||
? sessionCookie.attributes.maxAge
|
||||
: new TimeSpan(2, 'w').seconds(),
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
})
|
||||
const userId = await this.oauthService.handleOAuthUser(googleUser.id, googleUser.login, 'github')
|
||||
|
||||
return c.json({ message: 'ok' })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
// the specific error message depends on the provider
|
||||
if (error instanceof OAuth2RequestError) {
|
||||
// invalid code
|
||||
return c.body(null, 400)
|
||||
const session = await this.luciaService.lucia.createSession(userId, {})
|
||||
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id)
|
||||
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge:
|
||||
sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
|
||||
? sessionCookie.attributes.maxAge
|
||||
: new TimeSpan(2, 'w').seconds(),
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
})
|
||||
|
||||
return c.json({ message: 'ok' })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
// the specific error message depends on the provider
|
||||
if (error instanceof OAuth2RequestError) {
|
||||
// invalid code
|
||||
return c.body(null, 400)
|
||||
}
|
||||
return c.body(null, 500)
|
||||
}
|
||||
return c.body(null, 500)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,3 +127,8 @@ interface GitHubUser {
|
|||
id: number
|
||||
login: string
|
||||
}
|
||||
|
||||
interface GoogleUser {
|
||||
id: number
|
||||
login: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import env from "$lib/server/api/common/env";
|
||||
import { GitHub } from "arctic";
|
||||
import { GitHub, Google } from "arctic";
|
||||
|
||||
export const github = new GitHub(env.GITHUB_CLIENT_ID, env.GITHUB_CLIENT_SECRET);
|
||||
|
||||
export const google = new Google(env.GOOGLE_CLIENT_ID, env.GOOGLE_CLIENT_SECRET, `${env.ORIGIN}/auth/callback/google`);
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<h1>Privacy Policy</h1>
|
||||
<h2>Last Updated: September 13th, 2023</h2>
|
||||
<h2>Last Updated: September 19th, 2024</h2>
|
||||
|
||||
At Bored Game, we respect your privacy and are committed to protecting your personal information.
|
||||
We collect only the personal information that is necessary for us to provide our services to you.
|
||||
|
|
|
|||
|
|
@ -24,8 +24,3 @@ export async function GET(event: RequestEvent): Promise<Response> {
|
|||
|
||||
redirect(StatusCodes.TEMPORARY_REDIRECT, '/')
|
||||
}
|
||||
|
||||
interface GitHubUser {
|
||||
id: number
|
||||
login: string
|
||||
}
|
||||
|
|
|
|||
26
src/routes/(auth)/auth/callback/google/+server.ts
Normal file
26
src/routes/(auth)/auth/callback/google/+server.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { StatusCodes } from '$lib/constants/status-codes'
|
||||
import type { RequestEvent } from '@sveltejs/kit'
|
||||
import { redirect } from 'sveltekit-flash-message/server'
|
||||
|
||||
export async function GET(event: RequestEvent): Promise<Response> {
|
||||
const { locals, url } = event
|
||||
const code = url.searchParams.get('code')
|
||||
const state = url.searchParams.get('state')
|
||||
console.log('code', code, 'state', state)
|
||||
|
||||
const { data, error } = await locals.api.oauth.google.$get({ query: { code, state } }).then(locals.parseApiResponse)
|
||||
|
||||
if (error) {
|
||||
return new Response(JSON.stringify(error), {
|
||||
status: 400,
|
||||
})
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return new Response(JSON.stringify({ message: 'Invalid request' }), {
|
||||
status: 400,
|
||||
})
|
||||
}
|
||||
|
||||
redirect(StatusCodes.TEMPORARY_REDIRECT, '/')
|
||||
}
|
||||
|
|
@ -1,14 +1,25 @@
|
|||
import { github } from '$lib/server/auth'
|
||||
import { google } from '$lib/server/auth'
|
||||
import { redirect } from '@sveltejs/kit'
|
||||
import { generateState } from 'arctic'
|
||||
import { generateCodeVerifier, generateState } from 'arctic'
|
||||
|
||||
import type { RequestEvent } from '@sveltejs/kit'
|
||||
|
||||
// Google Login
|
||||
export async function GET(event: RequestEvent): Promise<Response> {
|
||||
const state = generateState()
|
||||
const url = await github.createAuthorizationURL(state)
|
||||
const codeVerifier = generateCodeVerifier();
|
||||
|
||||
event.cookies.set('github_oauth_state', state, {
|
||||
const url = await google.createAuthorizationURL(state, codeVerifier)
|
||||
|
||||
event.cookies.set('google_oauth_state', state, {
|
||||
path: '/',
|
||||
secure: import.meta.env.PROD,
|
||||
httpOnly: true,
|
||||
maxAge: 60 * 10,
|
||||
sameSite: 'lax',
|
||||
})
|
||||
|
||||
event.cookies.set('google_oauth_code_verifier', codeVerifier, {
|
||||
path: '/',
|
||||
secure: import.meta.env.PROD,
|
||||
httpOnly: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue