added logout

This commit is contained in:
2026-01-24 15:23:28 +11:00
parent c780050f8e
commit 9497c17c30
14 changed files with 327 additions and 191 deletions

View File

@@ -28,11 +28,9 @@ func Callback(
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
// Track callback redirect attempts
attempts, exceeded, track := store.TrackRedirect(r, "/callback", 5)
if exceeded {
// Build detailed error for logging
err := errors.Errorf(
"callback redirect loop detected after %d attempts | ip=%s ua=%s path=%s first_seen=%s",
attempts,
@@ -42,10 +40,8 @@ func Callback(
track.FirstSeen.Format("2006-01-02T15:04:05Z07:00"),
)
// Clear the tracking entry
store.ClearRedirectTrack(r, "/callback")
// Show error page
throwError(
server,
w,
@@ -66,23 +62,17 @@ func Callback(
}
data, err := verifyState(cfg.OAuth, w, r, state)
if err != nil {
// Check if this is a cookie error (401) or signature error (403)
if vsErr, ok := err.(*verifyStateError); ok {
if vsErr.IsCookieError() {
// Cookie missing/expired - normal failed/expired session (DEBUG)
throwUnauthorized(server, w, r, "OAuth session not found or expired", err)
} else {
// Signature verification failed - security violation (WARN)
throwForbiddenSecurity(server, w, r, "OAuth state verification failed", err)
}
} else {
// Unknown error type - treat as security issue
throwForbiddenSecurity(server, w, r, "OAuth state verification failed", err)
}
return
}
// SUCCESS POINT: State verified successfully
// Clear redirect tracking - OAuth callback completed successfully
store.ClearRedirectTrack(r, "/callback")
switch data {
@@ -108,10 +98,9 @@ func Callback(
)
}
// verifyStateError wraps an error with context about what went wrong
type verifyStateError struct {
err error
cookieError bool // true if cookie missing/invalid, false if signature invalid
cookieError bool
}
func (e *verifyStateError) Error() string {
@@ -135,20 +124,16 @@ func verifyState(
return "", errors.New("state param field is empty")
}
// Try to get the cookie
uak, err := oauth.GetStateCookie(r)
if err != nil {
// Cookie missing or invalid - this is a 401 (not authenticated)
return "", &verifyStateError{
err: errors.Wrap(err, "oauth.GetStateCookie"),
cookieError: true,
}
}
// Verify the state signature
data, err := oauth.VerifyState(cfg, state, uak)
if err != nil {
// Signature verification failed - this is a 403 (security violation)
return "", &verifyStateError{
err: errors.Wrap(err, "oauth.VerifyState"),
cookieError: false,
@@ -170,9 +155,9 @@ func login(
store *store.Store,
discordAPI *discord.APIClient,
) (func(), error) {
token, err := discord.AuthorizeWithCode(cfg.Discord, code, cfg.HWSAuth.TrustedHost, discordAPI)
token, err := discordAPI.AuthorizeWithCode(code)
if err != nil {
return nil, errors.Wrap(err, "discord.AuthorizeWithCode")
return nil, errors.Wrap(err, "discordAPI.AuthorizeWithCode")
}
session, err := discord.NewOAuthSession(token)
if err != nil {
@@ -204,6 +189,10 @@ func login(
})
redirect = "/register"
} else {
err = user.UpdateDiscordToken(ctx, tx, token)
if err != nil {
return nil, errors.Wrap(err, "user.UpdateDiscordToken")
}
err := auth.Login(w, r, user, true)
if err != nil {
return nil, errors.Wrap(err, "auth.Login")