Skip to content

Commit

Permalink
feat: oidc token refresher middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaspalma committed Dec 21, 2024
1 parent a2fe59c commit dfe02c6
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 9 deletions.
2 changes: 0 additions & 2 deletions website/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
PROD=0

TZ=UTC
PORT=3333
HOST=localhost
Expand Down
13 changes: 10 additions & 3 deletions website/app/controllers/oidc_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,18 @@ export default class OIDCController {
; ({ sub } = claims)

let userInfo = await client.fetchUserInfo(resolvedConfig, access_token, sub)

let user = await User.firstOrCreate({ email: userInfo.email });

let user = await User.firstOrCreate(
{
username: userInfo.preferred_username,
});

await auth.use('web').login(user);

return response.redirect().toPath('/');
return response
.cookie('access_token', tokens.access_token, { expires: new Date((new Date()).getTime() + (tokens.expires_in)) })
.cookie('refresh_token', tokens.refresh_token, { expires: new Date((new Date()).getTime() + (tokens.expires_in)) })
.redirect()
.toPath('/');
}
}
13 changes: 13 additions & 0 deletions website/app/lib/oidc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export async function oidcRenewTokens(refresh_token: string) {
return await fetch(`${process.env.OIDC_TOKEN_ENDPOINT}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(`${process.env.OIDC_CLIENT_ID}:${process.env.OIDC_CLIENT_SECRET}`)}`,
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refresh_token,
})
})
}
38 changes: 38 additions & 0 deletions website/app/middleware/oidc_token_refresher_middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
import * as jose from 'jose'

import { oidcRenewTokens } from '../lib/oidc.js';

export default class OidcTokenRefresherMiddleware {
async handle(ctx: HttpContext, next: NextFn) {
const { request } = ctx;

const refresh_token = jose.decodeJwt(request.cookie("refresh_token"));
const access_token = jose.decodeJwt(request.cookie("access_token"));

const refresh_token_expired = new Date(refresh_token.exp * 1000) < new Date();
const access_token_expired = new Date(access_token.exp * 1000) < new Date();

if ((refresh_token && access_token) && (refresh_token_expired || access_token_expired)) {
try {
const res = await oidcRenewTokens(refresh_token.refresh_token);

if (res.ok) {
const newTokens = await res.json();

ctx.response
.cookie('access_token', newTokens.access_token, { expires: new Date((new Date()).getTime() + (newTokens.expires_in)) })
.cookie('refresh_token', newTokens.refresh_token, { expires: new Date((new Date()).getTime() + (newTokens.expires_in)) })
}
} catch (e) {
console.error(e);
}
}

/**
* Call next method in the pipeline and return its output
*/
await next();
}
}
5 changes: 3 additions & 2 deletions website/app/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import hash from '@adonisjs/core/services/hash'
import { compose } from '@adonisjs/core/helpers'
import { BaseModel, column } from '@adonisjs/lucid/orm'
import { withAuthFinder } from '@adonisjs/auth/mixins/lucid'
import type { JsonValue } from 'openid-client'

const AuthFinder = withAuthFinder(() => hash.use('scrypt'), {
uids: ['email'],
Expand All @@ -12,9 +13,9 @@ const AuthFinder = withAuthFinder(() => hash.use('scrypt'), {
export default class User extends compose(BaseModel, AuthFinder) {
@column({ isPrimary: true })
declare id: number

@column()
declare fullName: string | null
declare username: string

@column()
declare email: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ export default class extends BaseSchema {
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').notNullable()
table.string('full_name').nullable()
table.string('email', 254).notNullable().unique()
table.string('username', 254).notNullable().unique()

table.timestamp('created_at').notNullable()
table.timestamp('updated_at').nullable()
Expand Down
2 changes: 2 additions & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"@radix-ui/react-toggle": "^1.1.1",
"@radix-ui/react-toggle-group": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.5",
"@types/node-jose": "^1.1.13",
"@vinejs/vine": "^3.0.0",
"better-sqlite3": "^11.7.0",
"class-variance-authority": "^0.7.1",
Expand All @@ -105,6 +106,7 @@
"edge.js": "^6.2.0",
"embla-carousel-react": "^8.5.1",
"input-otp": "^1.4.1",
"jose": "^5.9.6",
"lucide-react": "^0.468.0",
"luxon": "^3.5.0",
"next-themes": "^0.4.4",
Expand Down
13 changes: 13 additions & 0 deletions website/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions website/start/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ server.use([
() => import('@adonisjs/cors/cors_middleware'),
() => import('@adonisjs/vite/vite_middleware'),
() => import('@adonisjs/inertia/inertia_middleware'),
() => import('#middleware/oidc_token_refresher_middleware')
])

/**
Expand All @@ -46,6 +47,7 @@ router.use([
* the routes or the routes group.
*/
export const middleware = router.named({
oidcTokenRefresher: () => import('#middleware/oidc_token_refresher_middleware'),
guest: () => import('#middleware/guest_middleware'),
auth: () => import('#middleware/auth_middleware'),
})

0 comments on commit dfe02c6

Please sign in to comment.