Skip to content
Snippets Groups Projects

Trying to implement openid

Merged Thomas SAUVAGE requested to merge trying-to-implement-openid into main
Files
2
import Env from '@ioc:Adonis/Core/Env'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { SigmaUser } from 'App/Utils/types'
import ElementNotFoundException from 'App/Exceptions/ElementNotFoundException'
import AuthCodeVerifier from 'App/Models/AuthCodeVerifier'
import User from 'App/Models/User'
import { ClientMetadata, Issuer, generators } from 'openid-client'
const AUTH_URL = 'https://auth.binets.fr'
// ! Must be exactly the same as the one given to BR roots (with/without ending slash...)
const CALLBACK_URL = 'http://localhost:3333/auth/sigmaUser/callback/'
// Config for auth.binets.fr
const AUTH_URL = 'https://auth.binets.fr'
const clientOptions: ClientMetadata = {
client_id: 'localhost',
client_secret: Env.get('LOCALHOST_OPENID_CLIENT_SECRET'),
client_id: Env.get('AUTH_CLIENT_ID'),
client_secret: Env.get('AUTH_CLIENT_SECRET'),
redirect_uris: [CALLBACK_URL],
response_types: ['code'],
}
// ! Safe ? Works ? Risk of building up if people don't log in
let verifiers: { [state: string]: string } = {}
/** Login a user using `auth.binets.fr` which uses OpenId auth */
/** Login a user using `auth.binets.fr` which uses OpenId auth.
* The login is in 3 parts:
* - 1. Frontend asks for a url (call `loginSigmaUser`)
* - 2. User is redirected to this url, and he types his credentials
* - 3. `auth.binets.fr` sends a request to `callbackSigmaUser`
*/
export const loginSigmaUser = async ({ response }: HttpContextContract) => {
const issuer = await Issuer.discover(AUTH_URL)
const client = new issuer.Client(clientOptions)
const codeVerifier = generators.codeVerifier()
// Generate state and codeVerifier,
// used in the callback to verify the integrity of the request
const state = generators.state()
const codeVerifier = generators.codeVerifier()
verifiers[state] = codeVerifier
// Store the codeVerifier in the DB
AuthCodeVerifier.create({ state, codeVerifier })
// Generate the url
const codeChallenge = generators.codeChallenge(codeVerifier)
const url = client.authorizationUrl({
scope: 'openid email profile groups',
scope: 'openid email profile groups', // What info we want about the user
code_challenge: codeChallenge,
code_challenge_method: 'S256',
state,
@@ -45,27 +55,46 @@ export const callbackSigmaUser = async ({ response, request, auth }: HttpContext
const params = client.callbackParams(request.request)
if (!params.state) throw new Error("The response from the auth server doesn't have the state")
const codeVerifier = verifiers[params.state]
if (!params.state) throw new Error("The response from the auth server doesn't have a state")
// Get the codeVerifier from the DB
const { codeVerifier } = (await AuthCodeVerifier.find(params.state)) ?? {
codeVerifier: undefined,
}
if (!codeVerifier)
throw new ElementNotFoundException(
'Code verifier not found in the database, maybe the user took too long to register'
)
const tokenSet = await client.callback(CALLBACK_URL, params, {
code_verifier: codeVerifier,
state: params.state,
})
delete verifiers[params.state]
// Delete the codeVerifier
await AuthCodeVerifier.query().delete().where('state', params.state)
// Extract user infos from the response of `auth.binets.fr`
const { preferred_username: username, name, groups } = tokenSet.claims()
if (!username || !name || !groups)
throw new Error('The user given by the auth server is incomplete')
const receivedUser = {
username,
name,
groups: groups as string[], // ! UGLY
isSigmaUser: true,
isAdmin: false,
}
const userExtended = tokenSet.claims()
const user: SigmaUser = {
username: userExtended.preferred_username, // Unique ?
name: userExtended.name,
groups: userExtended.groups,
} as SigmaUser
// Add user to User db
const user = await User.firstOrCreate(receivedUser)
// console.log(tokenSet.claims())
// Generate token using Adonis JS default auth provider
const token = await auth.use('api').login(user)
// const user = ...
// const token = await auth.use('api').login(user)
// return token
return response.ok(tokenSet.claims())
// TODO: Redirect to the frontend with the token ?
return response.ok({ token, user })
}
Loading