separate subject from identifier
This commit is contained in:
parent
fd197907ab
commit
817d3f3175
|
@ -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"
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export interface User {
|
export interface User {
|
||||||
name: string
|
name: string
|
||||||
sub: string
|
sub: string
|
||||||
|
identifier: string
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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>.
|
||||||
|
Your identifier is <code>{data.user.identifier}</code>.
|
||||||
</p>
|
</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>
|
Loading…
Reference in a new issue