improve logging of OAuth login issues, decrease auth-code exchange timeout (#451)
Some checks failed
Docker / Build and Push (push) Has been cancelled
github-pages / deploy (push) Has been cancelled
Docker / release (push) Has been cancelled

This commit is contained in:
Christoph Haas 2025-06-10 17:51:45 +02:00
parent 61d8aa6589
commit e3b65ca337
No known key found for this signature in database
3 changed files with 80 additions and 66 deletions

View File

@ -57,6 +57,52 @@
}
}
},
"/auth/login/{provider}/callback": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Authentication"
],
"summary": "Handle the OAuth callback.",
"operationId": "auth_handleOauthCallbackGet",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/model.LoginProviderInfo"
}
}
}
}
}
},
"/auth/login/{provider}/init": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Authentication"
],
"summary": "Initiate the OAuth login flow.",
"operationId": "auth_handleOauthInitiateGet",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/model.LoginProviderInfo"
}
}
}
}
}
},
"/auth/logout": {
"post": {
"produces": [
@ -275,52 +321,6 @@
}
}
},
"/auth/{provider}/callback": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Authentication"
],
"summary": "Handle the OAuth callback.",
"operationId": "auth_handleOauthCallbackGet",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/model.LoginProviderInfo"
}
}
}
}
}
},
"/auth/{provider}/init": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Authentication"
],
"summary": "Initiate the OAuth login flow.",
"operationId": "auth_handleOauthInitiateGet",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/model.LoginProviderInfo"
}
}
}
}
}
},
"/config/frontend.js": {
"get": {
"produces": [
@ -2234,6 +2234,9 @@
"MailLinkOnly": {
"type": "boolean"
},
"MinPasswordLength": {
"type": "integer"
},
"PersistentConfigSupported": {
"type": "boolean"
},

View File

@ -383,6 +383,8 @@ definitions:
type: boolean
MailLinkOnly:
type: boolean
MinPasswordLength:
type: integer
PersistentConfigSupported:
type: boolean
SelfProvisioning:
@ -472,7 +474,22 @@ paths:
summary: Get all available audit entries. Ordered by timestamp.
tags:
- Audit
/auth/{provider}/callback:
/auth/login:
post:
operationId: auth_handleLoginPost
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/model.LoginProviderInfo'
type: array
summary: Get all available external login providers.
tags:
- Authentication
/auth/login/{provider}/callback:
get:
operationId: auth_handleOauthCallbackGet
produces:
@ -487,7 +504,7 @@ paths:
summary: Handle the OAuth callback.
tags:
- Authentication
/auth/{provider}/init:
/auth/login/{provider}/init:
get:
operationId: auth_handleOauthInitiateGet
produces:
@ -502,21 +519,6 @@ paths:
summary: Initiate the OAuth login flow.
tags:
- Authentication
/auth/login:
post:
operationId: auth_handleLoginPost
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/model.LoginProviderInfo'
type: array
summary: Get all available external login providers.
tags:
- Authentication
/auth/logout:
post:
operationId: auth_handleLogoutPost

View File

@ -2,6 +2,7 @@ package handlers
import (
"context"
"log/slog"
"net/http"
"net/url"
"strconv"
@ -189,7 +190,7 @@ func (e AuthEndpoint) handleSessionInfoGet() http.HandlerFunc {
// @Summary Initiate the OAuth login flow.
// @Produce json
// @Success 200 {object} []model.LoginProviderInfo
// @Router /auth/{provider}/init [get]
// @Router /auth/login/{provider}/init [get]
func (e AuthEndpoint) handleOauthInitiateGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
currentSession := e.session.GetData(r.Context())
@ -234,6 +235,8 @@ func (e AuthEndpoint) handleOauthInitiateGet() http.HandlerFunc {
authCodeUrl, state, nonce, err := e.authService.OauthLoginStep1(context.Background(), provider)
if err != nil {
slog.Debug("failed to create oauth auth code URL",
"provider", provider, "error", err)
if autoRedirect && e.isValidReturnUrl(returnTo) {
redirectToReturn()
} else {
@ -268,7 +271,7 @@ func (e AuthEndpoint) handleOauthInitiateGet() http.HandlerFunc {
// @Summary Handle the OAuth callback.
// @Produce json
// @Success 200 {object} []model.LoginProviderInfo
// @Router /auth/{provider}/callback [get]
// @Router /auth/login/{provider}/callback [get]
func (e AuthEndpoint) handleOauthCallbackGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
currentSession := e.session.GetData(r.Context())
@ -306,6 +309,8 @@ func (e AuthEndpoint) handleOauthCallbackGet() http.HandlerFunc {
oauthState := request.Query(r, "state")
if provider != currentSession.OauthProvider {
slog.Debug("invalid oauth provider in callback",
"expected", currentSession.OauthProvider, "got", provider, "state", oauthState)
if returnUrl != nil && e.isValidReturnUrl(returnUrl.String()) {
redirectToReturn()
} else {
@ -315,6 +320,8 @@ func (e AuthEndpoint) handleOauthCallbackGet() http.HandlerFunc {
return
}
if oauthState != currentSession.OauthState {
slog.Debug("invalid oauth state in callback",
"expected", currentSession.OauthState, "got", oauthState, "provider", provider)
if returnUrl != nil && e.isValidReturnUrl(returnUrl.String()) {
redirectToReturn()
} else {
@ -324,11 +331,13 @@ func (e AuthEndpoint) handleOauthCallbackGet() http.HandlerFunc {
return
}
loginCtx, cancel := context.WithTimeout(context.Background(), 1000*time.Second)
loginCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) // avoid long waits
user, err := e.authService.OauthLoginStep2(loginCtx, provider, currentSession.OauthNonce,
oauthCode)
cancel()
if err != nil {
slog.Debug("failed to process oauth code",
"provider", provider, "state", oauthState, "error", err)
if returnUrl != nil && e.isValidReturnUrl(returnUrl.String()) {
redirectToReturn()
} else {