separate subject from identifier

This commit is contained in:
May 2024-07-10 04:29:44 -07:00
parent fd197907ab
commit 817d3f3175
Signed by: split
GPG key ID: C325C61F0BF517C0
8 changed files with 82 additions and 31 deletions

View file

@ -9,9 +9,13 @@ OAUTH2__GET_TOKEN=
OAUTH2__CLIENT_ID=
# Client secret
OAUTH2__CLIENT_SECRET=
# OAuth2 scopes
OAUTH2__SCOPES=openid profile
# Userinfo route
USERINFO__ROUTE=
# Identifier you'd like to use to link avatars with
USERINFO__IDENTIFIER=preferred_username
# Prisma database URL
DATABASE_URL="file:./data.db"

BIN
bun.lockb

Binary file not shown.

View file

@ -27,6 +27,7 @@
"type": "module",
"dependencies": {
"@fontsource-variable/inter": "^5.0.18",
"@fontsource-variable/noto-sans-mono": "^5.0.20",
"@prisma/client": "5.16.2"
}
}

View file

@ -7,11 +7,13 @@ const configuration = {
},
client: {
id: process.env.OAUTH2__CLIENT_ID,
secret: process.env.OAUTH2__CLIENT_SECRET
secret: process.env.OAUTH2__CLIENT_SECRET,
scopes: process.env.OAUTH2__SCOPES
}
},
userinfo: {
route: process.env.USERINFO__ROUTE
route: process.env.USERINFO__ROUTE,
identifier: process.env.USERINFO__IDENTIFIER
}
}
export default configuration

View file

@ -92,41 +92,44 @@ export async function getUserInfo(id: string) {
})
if (!tokenInfo) return
let userInfo
// check for cached userinfo
if (userInfoCache.has(tokenInfo.owner))
return userInfoCache.get(tokenInfo.owner)
userInfo = userInfoCache.get(tokenInfo.owner)
else {
let userInfoRequest = await fetchUserInfo(tokenInfo.token)
if (!userInfoRequest.ok) {
// assume that token has expired.
// try fetching a new one
if (!userInfoRequest.ok) {
// assume that token has expired.
// try fetching a new one
if (!tokenInfo.refreshToken) return // no refresh token. back out
let token = await getNewToken({
grant_type: "refresh_token",
refresh_token: tokenInfo.refreshToken
})
if (!tokenInfo.refreshToken) return // no refresh token. back out
let token = await getNewToken({
grant_type: "refresh_token",
refresh_token: tokenInfo.refreshToken
})
if (!token) return // refresh failed. back out
prisma.token.update({
where: { id },
data: {
token: token.access_token,
refreshToken: token.refresh_token
}
})
if (!token) return // refresh failed. back out
prisma.token.update({
where: { id },
data: {
token: token.access_token,
refreshToken: token.refresh_token
}
})
userInfoRequest = await fetchUserInfo(token.access_token)
if (!userInfoRequest.ok) return // Give up
userInfoRequest = await fetchUserInfo(token.access_token)
if (!userInfoRequest.ok) return // Give up
}
userInfo = await userInfoRequest.json()
// cache userinfo
userInfoCache.set(tokenInfo.owner, userInfo)
setTimeout(() => userInfoCache.delete(tokenInfo.owner), 60*60*1000)
}
const userInfo = await userInfoRequest.json()
// cache userinfo
userInfoCache.set(tokenInfo.owner, userInfo)
setTimeout(() => userInfoCache.delete(tokenInfo.owner), 60*60*1000)
return userInfo as User
return { ...userInfo, identifier: userInfo[configuration.userinfo.identifier] } as User
}
export function deleteToken(id: string) {

View file

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

View file

@ -1,5 +1,6 @@
<script lang="ts">
import "@fontsource-variable/inter";
import "@fontsource-variable/noto-sans-mono"
import ava from "../assets/ava_icon.svg?raw"
import type { User } from "$lib/types";
export let data: { user?: User };
@ -15,12 +16,14 @@
--text: black;
--link: #333;
--background: white;
--crust: #eee;
}
@media (prefers-color-scheme:dark) {
:root {
--text: white;
--link: #aaa;
--background: #111;
--crust: #333;
}
}
html {
@ -54,6 +57,9 @@
a {
color: var(--link)
}
code {
font-family: "Space Mono", monospace, monospace;
}
</style>
</head>
<body>

View file

@ -4,7 +4,41 @@
export let data: {user: User};
</script>
<style>
form {
display: flex;
gap: 10px;
align-items: center;
}
form > * {
font-family: "Inter Variable", "Inter", sans-serif;
}
form > input[type="file"] {
width: 100%;
}
form > input[type="submit"], form > input[type="file"] {
padding: 0.5em 1em;
cursor: pointer;
border-radius: 8px;
border: 1px solid var(--link);
color: var(--text);
background-color: var(--crust);
}
form > input[type="file"]::file-selector-button {
display: none;
}
form > label {
flex-shrink: 0;
}
</style>
<h1>Hi, {data.user.name}</h1>
<p>
Your identifier is {data.user.sub}.
The <code>sub</code> claim is set to <code>{data.user.sub}</code>.
Your identifier is <code>{data.user.identifier}</code>.
</p>
<form method="post" enctype="multipart/form-data">
<label for="newAvatar">Set a new avatar:</label>
<input type="file" accept="image/*" name="newAvatar">
<input type="submit" value="Upload">
</form>