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= OAUTH2__CLIENT_ID=
# Client secret # Client secret
OAUTH2__CLIENT_SECRET= OAUTH2__CLIENT_SECRET=
# OAuth2 scopes
OAUTH2__SCOPES=openid profile
# Userinfo route # Userinfo route
USERINFO__ROUTE= USERINFO__ROUTE=
# Identifier you'd like to use to link avatars with
USERINFO__IDENTIFIER=preferred_username
# Prisma database URL # Prisma database URL
DATABASE_URL="file:./data.db" DATABASE_URL="file:./data.db"

BIN
bun.lockb

Binary file not shown.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,41 @@
export let data: {user: User}; export let data: {user: User};
</script> </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> <h1>Hi, {data.user.name}</h1>
<p> <p>
Your identifier is {data.user.sub}. The <code>sub</code> claim is set to <code>{data.user.sub}</code>.
</p> 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>