diff --git a/src/lib/guestbookAuth.ts b/src/lib/guestbookAuth.ts index e20d176..042952d 100644 --- a/src/lib/guestbookAuth.ts +++ b/src/lib/guestbookAuth.ts @@ -12,39 +12,109 @@ interface TokenResponse { scope: string, } -export const discord = { - name: 'discord', - getAuthUrl: (state: string, scopes: string[] = []) => { - const client_id = env.DISCORD_CLIENT_ID - const redir_uri = encodeURIComponent(callbackUrl) - const scope = scopes.join("+") - return `https://discord.com/oauth2/authorize?client_id=${client_id}&response_type=code&redirect_uri=${redir_uri}&scope=${scope}&state=${state}` - }, - getToken: async (code: string): Promise => { - const api = `https://discord.com/api/oauth2/token` - const body = new URLSearchParams({ - client_id: env.DISCORD_CLIENT_ID, - client_secret: env.DISCORD_CLIENT_SECRET, - grant_type: 'authorization_code', - redirect_uri: callbackUrl, - code, - }) - const resp = await fetch(api, { method: 'POST', body }) - if (resp.status !== 200) { - throw new Error("woopsies, couldnt get oauth token") - } - const tokenResp: any = await resp.json() +class OauthConfig { + clientId: string; + clientSecret: string; + + authUrl: URL; + tokenUrl: URL; + + joinScopes: (scopes: string[]) => string = (scopes) => scopes.join("+"); + getAuthParams: (params: Record, config: OauthConfig) => Record = (params) => { return params }; + getTokenParams: (params: Record, config: OauthConfig) => Record = (params) => { return params }; + extractTokenResponse: (tokenResp: any) => TokenResponse = (tokenResp) => { return { accessToken: tokenResp.access_token, tokenType: tokenResp.token_type, scope: tokenResp.scope, } - }, + }; + + tokenReqHeaders: Record = {}; + + constructor(clientId: string, clientSecret: string, authUrl: URL | string, tokenUrl: URL | string) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.authUrl = typeof authUrl === 'string' ? new URL(authUrl) : authUrl + this.tokenUrl = typeof tokenUrl === 'string' ? new URL(tokenUrl) : tokenUrl + } + + withJoinScopes(f: typeof this.joinScopes) { + this.joinScopes = f + return this + } + withGetAuthParams(f: typeof this.getAuthParams) { + this.getAuthParams = f + return this + } + withGetTokenParams(f: typeof this.getTokenParams) { + this.getTokenParams = f + return this + } + withExtractTokenResponse(f: typeof this.extractTokenResponse) { + this.extractTokenResponse = f + return this + } + withTokenRequestHeaders(f: typeof this.tokenReqHeaders) { + this.tokenReqHeaders = f + return this + } +} + +const genericOauthClient = (oauthConfig: OauthConfig) => { + return { + getAuthUrl: (state: string, scopes: string[] = []) => { + const redirect_uri = callbackUrl + const scope = oauthConfig.joinScopes(scopes) + const baseParams = { + client_id: oauthConfig.clientId, + redirect_uri, + scope, + state, + } + const params = oauthConfig.getAuthParams(baseParams, oauthConfig) + const urlParams = new URLSearchParams(params) + const urlRaw = `${oauthConfig.authUrl}?${urlParams.toString()}` + return new URL(urlRaw) + }, + getToken: async (code: string): Promise => { + const api = oauthConfig.tokenUrl + const baseParams = { + client_id: oauthConfig.clientId, + client_secret: oauthConfig.clientSecret, + redirect_uri: callbackUrl, + code, + } + const body = new URLSearchParams(oauthConfig.getTokenParams(baseParams, oauthConfig)) + const resp = await fetch(api, { method: 'POST', body, headers: oauthConfig.tokenReqHeaders }) + if (resp.status !== 200) { + throw new Error("woopsies, couldnt get oauth token") + } + const tokenResp: any = await resp.json() + return oauthConfig.extractTokenResponse(tokenResp) + } + } +} + +export const discord = { + name: 'discord', + ...genericOauthClient( + new OauthConfig( + env.DISCORD_CLIENT_ID, + env.DISCORD_CLIENT_SECRET, + 'https://discord.com/oauth2/authorize', + 'https://discord.com/api/oauth2/token', + ) + .withGetAuthParams((params) => { return { ...params, response_type: 'code', prompt: 'none' } }) + .withGetTokenParams((params) => { return { ...params, grant_type: 'authorization_code' } }) + ), identifyToken: async (tokenResp: TokenResponse): Promise => { const api = `https://discord.com/api/users/@me` - const resp = await fetch(api, {headers: { - 'Authorization': `${tokenResp.tokenType} ${tokenResp.accessToken}` - }}) + const resp = await fetch(api, { + headers: { + 'Authorization': `${tokenResp.tokenType} ${tokenResp.accessToken}` + } + }) if (resp.status !== 200) { throw new Error("woopsies, couldnt validate access token") } @@ -52,38 +122,26 @@ export const discord = { return body.username } } + export const github = { name: 'github', - getAuthUrl: (state: string, scopes: string[] = []) => { - const client_id = env.GITHUB_CLIENT_ID - const redir_uri = encodeURIComponent(callbackUrl) - const scope = encodeURIComponent(scopes.join(" ")) - return `https://github.com/login/oauth/authorize?client_id=${client_id}&redirect_uri=${redir_uri}&scope=${scope}&state=${state}` - }, - getToken: async (code: string): Promise => { - const api = `https://github.com/login/oauth/access_token` - const body = new URLSearchParams({ - client_id: env.GITHUB_CLIENT_ID, - client_secret: env.GITHUB_CLIENT_SECRET, - redirect_uri: callbackUrl, - code, - }) - const resp = await fetch(api, { method: 'POST', body, headers: { 'Accept': 'application/json' } }) - if (resp.status !== 200) { - throw new Error("woopsies, couldnt get oauth token") - } - const tokenResp: any = await resp.json() - return { - accessToken: tokenResp.access_token, - tokenType: tokenResp.token_type, - scope: tokenResp.scope, - } - }, + ...genericOauthClient( + new OauthConfig( + env.GITHUB_CLIENT_ID, + env.GITHUB_CLIENT_SECRET, + 'https://github.com/login/oauth/authorize', + 'https://github.com/login/oauth/access_token', + ) + .withJoinScopes((s) => { return s.join(" ") }) + .withTokenRequestHeaders({ 'Accept': 'application/json' }) + ), identifyToken: async (tokenResp: TokenResponse): Promise => { const api = `https://api.github.com/user` - const resp = await fetch(api, {headers: { - 'Authorization': `${tokenResp.tokenType} ${tokenResp.accessToken}` - }}) + const resp = await fetch(api, { + headers: { + 'Authorization': `${tokenResp.tokenType} ${tokenResp.accessToken}` + } + }) if (resp.status !== 200) { throw new Error("woopsies, couldnt validate access token") } @@ -130,10 +188,10 @@ export const getAuthClient = (name: string) => { switch (name) { case "discord": return discord - + case "github": return github - + default: return null } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 442a5b7..768dd4d 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -40,6 +40,14 @@ } const title = getTitle(data.route); + + const svgSquiggles = [ + [2], + [3], + [2], + [3], + [1], + ] @@ -65,26 +73,12 @@ - - - - - - - - - - - - - - - - - - - + {#each svgSquiggles as [scale], index} + + + + {/each} @@ -92,7 +86,7 @@ -