refactor: better auth code

This commit is contained in:
dusk 2024-08-24 22:52:04 +03:00
parent 76a058a312
commit ff7cf961f2
Signed by: dusk
SSH Key Fingerprint: SHA256:Abmvag+juovVufZTxyWY8KcVgrznxvBjQpJesv071Aw
2 changed files with 128 additions and 76 deletions

View File

@ -12,39 +12,109 @@ interface TokenResponse {
scope: string, scope: string,
} }
export const discord = { class OauthConfig {
name: 'discord', clientId: string;
getAuthUrl: (state: string, scopes: string[] = []) => { clientSecret: string;
const client_id = env.DISCORD_CLIENT_ID
const redir_uri = encodeURIComponent(callbackUrl) authUrl: URL;
const scope = scopes.join("+") tokenUrl: URL;
return `https://discord.com/oauth2/authorize?client_id=${client_id}&response_type=code&redirect_uri=${redir_uri}&scope=${scope}&state=${state}`
}, joinScopes: (scopes: string[]) => string = (scopes) => scopes.join("+");
getToken: async (code: string): Promise<TokenResponse> => { getAuthParams: (params: Record<string, string>, config: OauthConfig) => Record<string, string> = (params) => { return params };
const api = `https://discord.com/api/oauth2/token` getTokenParams: (params: Record<string, string>, config: OauthConfig) => Record<string, string> = (params) => { return params };
const body = new URLSearchParams({ extractTokenResponse: (tokenResp: any) => TokenResponse = (tokenResp) => {
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()
return { return {
accessToken: tokenResp.access_token, accessToken: tokenResp.access_token,
tokenType: tokenResp.token_type, tokenType: tokenResp.token_type,
scope: tokenResp.scope, scope: tokenResp.scope,
} }
}, };
tokenReqHeaders: Record<string, string> = {};
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<TokenResponse> => {
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<string> => { identifyToken: async (tokenResp: TokenResponse): Promise<string> => {
const api = `https://discord.com/api/users/@me` const api = `https://discord.com/api/users/@me`
const resp = await fetch(api, {headers: { const resp = await fetch(api, {
'Authorization': `${tokenResp.tokenType} ${tokenResp.accessToken}` headers: {
}}) 'Authorization': `${tokenResp.tokenType} ${tokenResp.accessToken}`
}
})
if (resp.status !== 200) { if (resp.status !== 200) {
throw new Error("woopsies, couldnt validate access token") throw new Error("woopsies, couldnt validate access token")
} }
@ -52,38 +122,26 @@ export const discord = {
return body.username return body.username
} }
} }
export const github = { export const github = {
name: 'github', name: 'github',
getAuthUrl: (state: string, scopes: string[] = []) => { ...genericOauthClient(
const client_id = env.GITHUB_CLIENT_ID new OauthConfig(
const redir_uri = encodeURIComponent(callbackUrl) env.GITHUB_CLIENT_ID,
const scope = encodeURIComponent(scopes.join(" ")) env.GITHUB_CLIENT_SECRET,
return `https://github.com/login/oauth/authorize?client_id=${client_id}&redirect_uri=${redir_uri}&scope=${scope}&state=${state}` 'https://github.com/login/oauth/authorize',
}, 'https://github.com/login/oauth/access_token',
getToken: async (code: string): Promise<TokenResponse> => { )
const api = `https://github.com/login/oauth/access_token` .withJoinScopes((s) => { return s.join(" ") })
const body = new URLSearchParams({ .withTokenRequestHeaders({ 'Accept': 'application/json' })
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,
}
},
identifyToken: async (tokenResp: TokenResponse): Promise<string> => { identifyToken: async (tokenResp: TokenResponse): Promise<string> => {
const api = `https://api.github.com/user` const api = `https://api.github.com/user`
const resp = await fetch(api, {headers: { const resp = await fetch(api, {
'Authorization': `${tokenResp.tokenType} ${tokenResp.accessToken}` headers: {
}}) 'Authorization': `${tokenResp.tokenType} ${tokenResp.accessToken}`
}
})
if (resp.status !== 200) { if (resp.status !== 200) {
throw new Error("woopsies, couldnt validate access token") throw new Error("woopsies, couldnt validate access token")
} }
@ -130,10 +188,10 @@ export const getAuthClient = (name: string) => {
switch (name) { switch (name) {
case "discord": case "discord":
return discord return discord
case "github": case "github":
return github return github
default: default:
return null return null
} }

View File

@ -40,6 +40,14 @@
} }
const title = getTitle(data.route); const title = getTitle(data.route);
const svgSquiggles = [
[2],
[3],
[2],
[3],
[1],
]
</script> </script>
<svelte:head> <svelte:head>
@ -65,26 +73,12 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="absolute -z-50"> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="absolute -z-50">
<defs> <defs>
<filter id="squiggly-0"> {#each svgSquiggles as [scale], index}
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="0" /> <filter id="squiggly-{index}">
<feDisplacementMap id="displacement" in="SourceGraphic" in2="noise" scale="2" /> <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed={index} />
</filter> <feDisplacementMap in="SourceGraphic" in2="noise" {scale} />
<filter id="squiggly-1">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="1" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="3" />
</filter>
<filter id="squiggly-2">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="2" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" />
</filter>
<filter id="squiggly-3">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="3" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="3" />
</filter>
<filter id="squiggly-4">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="4" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="1" />
</filter> </filter>
{/each}
</defs> </defs>
</svg> </svg>
@ -92,7 +86,7 @@
<slot /> <slot />
</div> </div>
<nav class="w-full max-h-[6vh] fixed bottom-0 z-10 bg-ralsei-black"> <nav class="w-full min-h-[5vh] max-h-[6vh] fixed bottom-0 z-10 bg-ralsei-black">
<div <div
class=" class="
max-w-full max-h-fit p-1 overflow-auto max-w-full max-h-fit p-1 overflow-auto