Revamp OIDC auth: remove Passport wrapper, add schema-driven settings UI

- Remove Passport.js wrapper from OIDC auth, use openid-client directly
- Add schema-driven OIDC settings UI (OidcSettingsSchema.js drives form rendering)
- Add group mapping with KeyValueEditor (explicit mapping or legacy direct name match)
- Add scopes configuration (authOpenIDScopes)
- Add verified email enforcement option (authOpenIDRequireVerifiedEmail)
- Fix group claim validation rejecting URN-style claims (#4744)
- Add auto-discover endpoint for OIDC provider configuration
- Store oidcIdToken in sessions table instead of cookie
- Add AuthError class for structured error handling in auth flows
- Migration v2.33.0 adds oidcIdToken column and new settings fields
This commit is contained in:
Denis Arnst 2026-02-05 17:54:59 +01:00
parent fe13456a2b
commit 33bee70a12
No known key found for this signature in database
GPG key ID: D5866C58940197BF
16 changed files with 1554 additions and 571 deletions

View file

@ -156,9 +156,10 @@ class TokenManager {
*
* @param {{ id:string, username:string }} user
* @param {import('express').Request} req
* @param {string|null} [oidcIdToken=null] - OIDC id_token to store in session for logout
* @returns {Promise<{ accessToken:string, refreshToken:string, session:import('../models/Session') }>}
*/
async createTokensAndSession(user, req) {
async createTokensAndSession(user, req, oidcIdToken = null) {
const ipAddress = requestIp.getClientIp(req)
const userAgent = req.headers['user-agent']
const accessToken = this.generateTempAccessToken(user)
@ -167,7 +168,7 @@ class TokenManager {
// Calculate expiration time for the refresh token
const expiresAt = new Date(Date.now() + this.RefreshTokenExpiry * 1000)
const session = await Database.sessionModel.createSession(user.id, ipAddress, userAgent, refreshToken, expiresAt)
const session = await Database.sessionModel.createSession(user.id, ipAddress, userAgent, refreshToken, expiresAt, oidcIdToken)
return {
accessToken,
@ -392,6 +393,17 @@ class TokenManager {
return null
}
/**
* Get a session by its refresh token
*
* @param {string} refreshToken
* @returns {Promise<import('../models/Session')|null>}
*/
async getSessionByRefreshToken(refreshToken) {
if (!refreshToken) return null
return await Database.sessionModel.findOne({ where: { refreshToken } })
}
/**
* Invalidate a refresh token - used for logout
*