/* * Copyright (C) 2023 Jonni Liljamo * * This file is licensed under AGPL-3.0-or-later, see NOTICE and LICENSE for * more information. */ package handlers import ( "context" "log" "net/http" "tixe/auth" "tixe/db" "tixe/types" "tixe/util" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5" "github.com/oklog/ulid/v2" ) func AuthCallback(auth *auth.Auth) gin.HandlerFunc { return func(c *gin.Context) { session := sessions.Default(c) if c.Query("state") != session.Get("state") { errStr := "Invalid state parameter" util.RenderError(c, "state error", errStr, nil) return } token, err := auth.Exchange(c.Request.Context(), c.Query("code")) if err != nil { errStr := "Failed to exchange authorization code for token" util.RenderError(c, "token error", errStr, nil) return } idToken, err := auth.VerifyIDToken(c.Request.Context(), token) if err != nil { errStr := "Failed to verify ID token" util.RenderError(c, "token error", errStr, nil) return } var profile map[string]interface{} if err := idToken.Claims(&profile); err != nil { errStr := "Failed to get claims" util.RenderError(c, "claims error", errStr, nil) return } // Try to get the relevant details of the user var userId, userDisplayName string // idToken.Subject should be unique and should not change. // I think. Maybe. Possibly. Hopefully. scanErr := db.PgPool.QueryRow(context.Background(), "SELECT id, display_name FROM users WHERE oidc_subject = $1", idToken.Subject).Scan(&userId, &userDisplayName) if scanErr == pgx.ErrNoRows { log.Printf("[tixe/auth] New user detected logging in, creating entry in database") // The user does not exist in the db, create it userId = ulid.Make().String() userDisplayName = profile["name"].(string) _, err = db.PgPool.Exec(context.Background(), "INSERT INTO users(id, display_name, oidc_subject) VALUES($1, $2, $3)", userId, userDisplayName, idToken.Subject) if err != nil { errStr := "Could not create database entry for oidc user" log.Printf("[tixe/auth] ERROR: %s", errStr) util.RenderError(c, "db error", errStr, nil) return } } else if scanErr != nil { // scanErr was some other error than ErrNoRows errStr := "Could not query database for oidc user" log.Printf("[tixe/auth] ERROR: %s: %v", errStr, scanErr) util.RenderError(c, "db error", errStr, nil) return } // The user.Id field is read in other requests to read user data from the db session.Set("user", types.User { Id: userId, DisplayName: userDisplayName }) session.Set("access_token", token.AccessToken) session.Set("profile", profile) if err := session.Save(); err != nil { errStr := "Failed to save session" log.Printf("[tixe/auth] ERROR: %s: %v", errStr, err) util.RenderError(c, "session error", errStr, nil) return } c.Redirect(http.StatusTemporaryRedirect, "/") } }