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,
}
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<TokenResponse> => {
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<string, string>, config: OauthConfig) => Record<string, string> = (params) => { return params };
getTokenParams: (params: Record<string, string>, config: OauthConfig) => Record<string, string> = (params) => { return params };
extractTokenResponse: (tokenResp: any) => TokenResponse = (tokenResp) => {
return {
accessToken: tokenResp.access_token,
tokenType: tokenResp.token_type,
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> => {
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<TokenResponse> => {
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<string> => {
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
}

View File

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