merge authconfig

This commit is contained in:
jay brown
2025-04-11 11:13:07 -07:00
parent 4f40a7e6ea
commit f620118b59
10 changed files with 135 additions and 266 deletions
+40 -37
View File
@@ -7,6 +7,9 @@ import (
"net/http"
"net/url"
"os"
"queryorchestration/internal/serviceconfig/auth"
"strings"
"time"
@@ -15,18 +18,18 @@ import (
// initiateLoginWithPKCE generates PKCE code challenge and redirects to Cognito
// This function starts the OAuth authorization flow with PKCE
func initiateLoginWithPKCE(c echo.Context, config *CognitoConfig) error {
func initiateLoginWithPKCE(c echo.Context, config *auth.CognitoConfig) error {
// Generate random state parameter to prevent CSRF
state, err := generateRandomString(32)
if err != nil {
config.Logger.Error("Failed to generate state parameter", "error", err)
config.AuthLogger.Error("Failed to generate state parameter", "error", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Authentication initialization failed"})
}
// Generate code verifier (random string between 43-128 chars)
codeVerifier, err := generateRandomString(64)
if err != nil {
config.Logger.Error("Failed to generate code verifier", "error", err)
config.AuthLogger.Error("Failed to generate code verifier", "error", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Authentication initialization failed"})
}
@@ -34,13 +37,13 @@ func initiateLoginWithPKCE(c echo.Context, config *CognitoConfig) error {
codeChallenge := createCodeChallenge(codeVerifier)
// Store in session for later verification (with expiration time)
storePKCESession(state, codeVerifier, config.Logger)
storePKCESession(state, codeVerifier, config.AuthLogger)
// Build authorization URL with PKCE parameters
params := url.Values{}
params.Set("client_id", config.ClientID)
params.Set("client_id", config.AuthClientID)
params.Set("response_type", "code")
params.Set("redirect_uri", config.RedirectURI)
params.Set("redirect_uri", config.AuthRedirectURI)
params.Set("scope", "openid email profile")
params.Set("state", state)
params.Set("code_challenge", codeChallenge)
@@ -49,11 +52,11 @@ func initiateLoginWithPKCE(c echo.Context, config *CognitoConfig) error {
authURL := fmt.Sprintf("%s?%s", config.AuthURL, params.Encode())
if os.Getenv("DEBUG") == "true" {
config.Logger.Debug("Initiating login with PKCE",
config.AuthLogger.Debug("Initiating login with PKCE",
"code_verifier", codeVerifier,
"code_challenge", codeChallenge,
"state", state,
"redirect_uri", config.RedirectURI)
"redirect_uri", config.AuthRedirectURI)
}
// Redirect user to Cognito login page
@@ -63,20 +66,20 @@ func initiateLoginWithPKCE(c echo.Context, config *CognitoConfig) error {
// exchangeCodeForTokensWithPKCE exchanges the authorization code for tokens using PKCE
// Called during the OAuth callback flow to get tokens from the authorization code
// Makes an HTTP request to Cognito's token endpoint to perform this exchange
func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config CognitoConfig) (*TokenResponse, error) {
func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config auth.CognitoConfig) (*TokenResponse, error) {
data := url.Values{}
data.Set("grant_type", "authorization_code")
data.Set("client_id", config.ClientID)
data.Set("client_id", config.AuthClientID)
data.Set("code", authCode)
data.Set("redirect_uri", config.RedirectURI)
data.Set("redirect_uri", config.AuthRedirectURI)
data.Set("code_verifier", codeVerifier) // Include code verifier for PKCE
config.Logger.Debug("Token request details",
"url", config.TokenURL,
"client_id", config.ClientID,
"redirect_uri", config.RedirectURI)
config.AuthLogger.Debug("Token request details",
"url", config.AuthTokenURL,
"client_id", config.AuthClientID,
"redirect_uri", config.AuthRedirectURI)
req, err := http.NewRequest("POST", config.TokenURL, strings.NewReader(data.Encode()))
req, err := http.NewRequest("POST", config.AuthTokenURL, strings.NewReader(data.Encode()))
if err != nil {
return nil, fmt.Errorf("failed to create token request: %w", err)
}
@@ -84,11 +87,11 @@ func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config Cognito
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// Add Authorization header if client secret is provided
if config.ClientSecret != "" {
req.SetBasicAuth(config.ClientID, config.ClientSecret)
config.Logger.Debug("Using Basic Auth authentication")
if config.AuthClientSecret != "" {
req.SetBasicAuth(config.AuthClientID, config.AuthClientSecret)
config.AuthLogger.Debug("Using Basic Auth authentication")
} else {
config.Logger.Debug("Using public client authentication (no secret)")
config.AuthLogger.Debug("Using public client authentication (no secret)")
}
client := &http.Client{Timeout: 10 * time.Second}
@@ -118,7 +121,7 @@ func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config Cognito
// handleOAuthCallback handles the OAuth 2.0 authorization code flow callback with PKCE
// Called when Cognito redirects back to our application with an authorization code
// Exchanges the code for tokens, verifies them, and redirects to home page with token in cookie
func handleOAuthCallback(c echo.Context, config *CognitoConfig) error {
func handleOAuthCallback(c echo.Context, config *auth.CognitoConfig) error {
// Extract the authorization code and state
code := c.QueryParam("code")
if code == "" {
@@ -141,52 +144,52 @@ func handleOAuthCallback(c echo.Context, config *CognitoConfig) error {
}
// Retrieve stored code verifier
codeVerifier, err := getCodeVerifier(state, config.Logger)
codeVerifier, err := getCodeVerifier(state, config.AuthLogger)
if err != nil {
config.Logger.Error("Failed to retrieve code verifier", "error", err, "state", state)
config.AuthLogger.Error("Failed to retrieve code verifier", "error", err, "state", state)
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid or expired session"})
}
// Exchange the code for tokens using PKCE
tokens, err := exchangeCodeForTokensWithPKCE(code, codeVerifier, *config)
if err != nil {
config.Logger.Error("Failed to exchange code for tokens", "error", err)
config.AuthLogger.Error("Failed to exchange code for tokens", "error", err)
return c.JSON(http.StatusUnauthorized, map[string]string{"error": err.Error()})
}
// Get the JWKS for token verification
keySet, err := GetJWKS(config.JwksURL, config.Logger)
keySet, err := GetJWKS(config.AuthJwksURL, config.AuthLogger)
if err != nil {
config.Logger.Error("Failed to fetch JWKS", "error", err)
config.AuthLogger.Error("Failed to fetch JWKS", "error", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to verify token"})
}
// Verify ID token
idTokenClaims, err := verifyToken(tokens.IDToken, keySet, *config)
if err != nil {
config.Logger.Error("Failed to verify ID token", "error", err)
config.AuthLogger.Error("Failed to verify ID token", "error", err)
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Invalid token"})
}
// Extract user groups
userGroups, err := GetUserGroups(idTokenClaims)
if err != nil {
config.Logger.Warn("Failed to extract user groups", "error", err)
config.AuthLogger.Warn("Failed to extract user groups", "error", err)
userGroups = []string{} // Initialize as empty array
}
// Debug logging for tokens
if os.Getenv("DEBUG") == "true" {
// Log decoded token for debugging
config.Logger.Info("ID Token Claims", "claims", idTokenClaims)
config.Logger.Info("Raw ID Token", "token", tokens.IDToken)
config.Logger.Info("Raw Access Token", "token", tokens.AccessToken)
config.AuthLogger.Info("ID Token Claims", "claims", idTokenClaims)
config.AuthLogger.Info("Raw ID Token", "token", tokens.IDToken)
config.AuthLogger.Info("Raw Access Token", "token", tokens.AccessToken)
}
// Check authorization
authorized, requiredGroups := checkPermissions(config.CallbackPath, userGroups, config.RoutePermissions, config.Logger)
authorized, requiredGroups := checkPermissions(config.AuthCallbackPath, userGroups, config.AuthRoutePermissions, config.AuthLogger)
if !authorized {
config.Logger.Warn("OAuth callback - access denied",
config.AuthLogger.Warn("OAuth callback - access denied",
"user", idTokenClaims["cognito:username"],
"groups", userGroups,
"required", requiredGroups)
@@ -221,14 +224,14 @@ func handleOAuthCallback(c echo.Context, config *CognitoConfig) error {
// Extract username for display purposes (optional)
username, _ := idTokenClaims["cognito:username"].(string)
config.Logger.Info("User authenticated successfully", "username", username, "groups", userGroups)
config.AuthLogger.Info("User authenticated successfully", "username", username, "groups", userGroups)
// Redirect to home page
return c.Redirect(http.StatusFound, config.HomePath)
return c.Redirect(http.StatusFound, config.AuthHomePath)
}
// LogoutHandler handles the logout process by clearing the auth cookie
func LogoutHandler(c echo.Context, config *CognitoConfig) error {
func LogoutHandler(c echo.Context, config *auth.CognitoConfig) error {
// Clear the token cookie
cookie := new(http.Cookie)
cookie.Name = "auth_token"
@@ -239,5 +242,5 @@ func LogoutHandler(c echo.Context, config *CognitoConfig) error {
c.SetCookie(cookie)
// Redirect to home page
return c.Redirect(http.StatusFound, config.HomePath)
return c.Redirect(http.StatusFound, config.AuthHomePath)
}