Skip to content
Snippets Groups Projects
Unverified Commit bf3a901a authored by Thomas SAUVAGE's avatar Thomas SAUVAGE
Browse files

Doc & structure

parent 8cbf08ac
No related branches found
No related tags found
1 merge request!4Trying to implement openid
Pipeline #13844 passed
...@@ -11,7 +11,8 @@ PG_DB_NAME=bp_dev ...@@ -11,7 +11,8 @@ PG_DB_NAME=bp_dev
PG_PASSWORD=OEBFIUZGBfbuvfeybUEFQUyvfgevf PG_PASSWORD=OEBFIUZGBfbuvfeybUEFQUyvfgevf
# Ask the BR to have it # Ask the BR to have it
LOCALHOST_OPENID_CLIENT_SECRET=... AUTH_CLIENT_ID=localhost
AUTH_CLIENT_SECRET=...
# Secret used to make crons # Secret used to make crons, generate a random string
CRON_TOKEN=... CRON_TOKEN=...
\ No newline at end of file
...@@ -4,7 +4,7 @@ import { randomString } from 'App/Utils/random' ...@@ -4,7 +4,7 @@ import { randomString } from 'App/Utils/random'
/** Login a user that is not using the CAS */ /** Login a user that is not using the CAS */
export const loginNotSigmaUser = async ({ request, response, auth }: HttpContextContract) => { export const loginNotSigmaUser = async ({ request, response, auth }: HttpContextContract) => {
// TODO: Validate ? // TODO: Validate
const username = request.input('username') const username = request.input('username')
const password = request.input('password') const password = request.input('password')
...@@ -19,7 +19,7 @@ export const loginNotSigmaUser = async ({ request, response, auth }: HttpContext ...@@ -19,7 +19,7 @@ export const loginNotSigmaUser = async ({ request, response, auth }: HttpContext
/** Create a user that is not using the CAS */ /** Create a user that is not using the CAS */
export const createNotSigmaUser = async ({ request, response }: HttpContextContract) => { export const createNotSigmaUser = async ({ request, response }: HttpContextContract) => {
// TODO: Validate ? // TODO: Validate
const username = request.input('username') const username = request.input('username')
const name = request.input('name') const name = request.input('name')
......
...@@ -4,36 +4,40 @@ import AuthCodeVerifier from 'App/Models/AuthCodeVerifier' ...@@ -4,36 +4,40 @@ import AuthCodeVerifier from 'App/Models/AuthCodeVerifier'
import User from 'App/Models/User' import User from 'App/Models/User'
import { ClientMetadata, Issuer, generators } from 'openid-client' 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...) // ! 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/' const CALLBACK_URL = 'http://localhost:3333/auth/sigmaUser/callback/'
// Config for auth.binets.fr
const AUTH_URL = 'https://auth.binets.fr'
const clientOptions: ClientMetadata = { const clientOptions: ClientMetadata = {
client_id: 'localhost', client_id: Env.get('AUTH_CLIENT_ID'),
client_secret: Env.get('LOCALHOST_OPENID_CLIENT_SECRET'), client_secret: Env.get('AUTH_CLIENT_SECRET'),
redirect_uris: [CALLBACK_URL], redirect_uris: [CALLBACK_URL],
response_types: ['code'], response_types: ['code'],
} }
// ! Safe ? Works ? Risk of building up if people don't log in /** Login a user using `auth.binets.fr` which uses OpenId auth.
// TODO: Make a DB, with a lifespan for each item * The login is in 3 parts:
let verifiers: { [state: string]: string } = {} * - 1. Frontend asks for a url (call `loginSigmaUser`)
* - 2. User is redirected to this url, and he types his credentials
/** Login a user using `auth.binets.fr` which uses OpenId auth */ * - 3. `auth.binets.fr` sends a request to `callbackSigmaUser`
*/
export const loginSigmaUser = async ({ response }: HttpContextContract) => { export const loginSigmaUser = async ({ response }: HttpContextContract) => {
const issuer = await Issuer.discover(AUTH_URL) const issuer = await Issuer.discover(AUTH_URL)
const client = new issuer.Client(clientOptions) const client = new issuer.Client(clientOptions)
const codeVerifier = generators.codeVerifier() // Generate and store a codeVerifier,
// used in the callback to verify the integrity of the request
const state = generators.state() const state = generators.state()
const codeVerifier = generators.codeVerifier()
verifiers[state] = codeVerifier
AuthCodeVerifier.create({ state, codeVerifier }) AuthCodeVerifier.create({ state, codeVerifier })
const codeChallenge = generators.codeChallenge(codeVerifier) const codeChallenge = generators.codeChallenge(codeVerifier)
// Generate the url
const url = client.authorizationUrl({ 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: codeChallenge,
code_challenge_method: 'S256', code_challenge_method: 'S256',
state, state,
...@@ -48,8 +52,9 @@ export const callbackSigmaUser = async ({ response, request, auth }: HttpContext ...@@ -48,8 +52,9 @@ export const callbackSigmaUser = async ({ response, request, auth }: HttpContext
const params = client.callbackParams(request.request) const params = client.callbackParams(request.request)
if (!params.state) throw new Error("The response from the auth server doesn't have the 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.findOrFail(params.state) const { codeVerifier } = await AuthCodeVerifier.findOrFail(params.state)
const tokenSet = await client.callback(CALLBACK_URL, params, { const tokenSet = await client.callback(CALLBACK_URL, params, {
...@@ -60,21 +65,24 @@ export const callbackSigmaUser = async ({ response, request, auth }: HttpContext ...@@ -60,21 +65,24 @@ export const callbackSigmaUser = async ({ response, request, auth }: HttpContext
// Delete the codeVerifier // Delete the codeVerifier
await AuthCodeVerifier.query().delete().where('state', params.state) 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() const { preferred_username: username, name, groups } = tokenSet.claims()
console.log(tokenSet.claims())
if (!username || !name || !groups) if (!username || !name || !groups)
throw new Error('The user given by the auth server is incomplete') throw new Error('The user given by the auth server is incomplete')
const receivedUser = { const receivedUser = {
username, // Unique ? username,
name, name,
groups: groups as string[], // ! UGLY groups: groups as string[], // ! UGLY
isSigmaUser: true, isSigmaUser: true,
isAdmin: false, isAdmin: false,
} }
// Add user to User db
const user = await User.firstOrCreate(receivedUser) const user = await User.firstOrCreate(receivedUser)
// Generate token using Adonis JS default auth provider
const token = await auth.use('api').login(user) const token = await auth.use('api').login(user)
return response.ok({ token, user }) return response.ok({ token, user })
......
...@@ -28,6 +28,8 @@ export default Env.rules({ ...@@ -28,6 +28,8 @@ export default Env.rules({
PG_PASSWORD: Env.schema.string.optional(), PG_PASSWORD: Env.schema.string.optional(),
PG_DB_NAME: Env.schema.string(), PG_DB_NAME: Env.schema.string(),
LOCALHOST_OPENID_CLIENT_SECRET: Env.schema.string(), AUTH_CLIENT_ID: Env.schema.string(),
AUTH_CLIENT_SECRET: Env.schema.string(),
CRON_TOKEN: Env.schema.string(), CRON_TOKEN: Env.schema.string(),
}) })
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment