Request tokens

This commit is contained in:
split / May 2024-07-09 19:57:44 -07:00
parent c61a32369c
commit d3d9e87ce0
Signed by: split
GPG key ID: C325C61F0BF517C0
3 changed files with 69 additions and 8 deletions

View file

@ -1,8 +1,13 @@
import { redirect } from "@sveltejs/kit"
import { error, redirect, type Cookies } from "@sveltejs/kit"
import configuration from "./configuration"
const states = new Map<string, ReturnType<typeof setTimeout>>()
// Map of OAuth2 states
const states = new Map<string, { redirect_uri: string, timeout: ReturnType<typeof setTimeout> }>()
/**
* @description Launch an OAuth2 login request for this request.
* @param req Request to launch the login for; required to obtain the Host header.
*/
export function launchLogin(req: Request) {
// Create a state to be used in the OAuth2 authorization request
const state = crypto.randomUUID()
@ -11,7 +16,7 @@ export function launchLogin(req: Request) {
const searchParams = new URLSearchParams({
response_type: "code",
client_id: configuration.oauth2.client.id,
redirect_uri: new URL(`/set`, req.url).toString(),
redirect_uri: req.url,
scope: "openid profile email",
state
})
@ -20,9 +25,61 @@ export function launchLogin(req: Request) {
`?${searchParams.toString()}`,
configuration.oauth2.endpoints.authenticate
)
states
.set(state, setTimeout(() => states.delete(state), 60000))
return redirect(302, target.toString())
// cache state
// NO IDEA IF THIS WORKS IN SERVERLESS LOL
// not like this is going to be running serverless anyway
states
.set(
state,
{
timeout: setTimeout(
() => states.delete(state),
2*60*1000
),
redirect_uri: req.url
}
)
throw redirect(302, target.toString())
}
/**
* @description Request a token from the OAuth server
* @param params
* @returns Access token, its time-to-expiration, and refresh token if applicable
*/
export async function retrieveToken(
params:
{grant_type: "authorization_code", redirect_uri: string, code: string}
| {grant_type: "refresh_token", refresh_token: string}
) {
// Generate a query string for the request
const searchParams = new URLSearchParams({
...params,
client_id: configuration.oauth2.client.id,
client_secret: configuration.oauth2.client.secret
})
const url = new URL(
`?${searchParams.toString()}`,
configuration.oauth2.endpoints.token
)
let res = await fetch(url)
if (!res.ok)
throw error(401, "Couldn't retrieve token for user")
else
return (await res.json()) as { access_token: string, expires_in: number, refresh_token?: string }
}
export async function getRequestUser(request: Request, cookies: Cookies) {
const params = new URLSearchParams(request.url.split("?").slice(1).join("?"))
let token = cookies.get("token")
if (!token && params.has("code") && params.has("state")) {
if (!states.has(params.get("state")!))
throw error(401, "bad state")
token = params.get("code")!
cookies.set("token", token, { path: "/" })
}
}

4
src/lib/types.ts Normal file
View file

@ -0,0 +1,4 @@
export interface User {
name: string
sub: string
}

View file

@ -3,5 +3,5 @@ export async function load({ request, parent }) {
//const { user } = await parent();
let user = null
if (!user)
throw launchLogin(request)
launchLogin(request)
}