partial working

This commit is contained in:
jay brown
2025-04-11 16:46:05 -07:00
parent ba6f624299
commit 61accd4dd4
7 changed files with 100 additions and 88 deletions
+40 -40
View File
@@ -18,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 *auth.CognitoConfig) error {
func initiateLoginWithPKCE(c echo.Context, config auth.ConfigProvider) error {
// Generate random state parameter to prevent CSRF
state, err := generateRandomString(32)
if err != nil {
config.AuthLogger.Error("Failed to generate state parameter", "error", err)
config.GetAuthLogger().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.AuthLogger.Error("Failed to generate code verifier", "error", err)
config.GetAuthLogger().Error("Failed to generate code verifier", "error", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Authentication initialization failed"})
}
@@ -37,26 +37,26 @@ func initiateLoginWithPKCE(c echo.Context, config *auth.CognitoConfig) error {
codeChallenge := createCodeChallenge(codeVerifier)
// Store in session for later verification (with expiration time)
storePKCESession(state, codeVerifier, config.AuthLogger)
storePKCESession(state, codeVerifier, config.GetAuthLogger())
// Build authorization URL with PKCE parameters
params := url.Values{}
params.Set("client_id", config.AuthClientID)
params.Set("client_id", config.GetAuthClientID())
params.Set("response_type", "code")
params.Set("redirect_uri", config.AuthRedirectURI)
params.Set("redirect_uri", config.GetAuthRedirectURI())
params.Set("scope", "openid email profile")
params.Set("state", state)
params.Set("code_challenge", codeChallenge)
params.Set("code_challenge_method", "S256")
authURL := fmt.Sprintf("%s?%s", config.AuthURL, params.Encode())
authURL := fmt.Sprintf("%s?%s", config.GetAuthURL(), params.Encode())
if os.Getenv("DEBUG") == "true" {
config.AuthLogger.Debug("Initiating login with PKCE",
config.GetAuthLogger().Debug("Initiating login with PKCE",
"code_verifier", codeVerifier,
"code_challenge", codeChallenge,
"state", state,
"redirect_uri", config.AuthRedirectURI)
"redirect_uri", config.GetAuthRedirectURI())
}
// Redirect user to Cognito login page
@@ -66,20 +66,20 @@ func initiateLoginWithPKCE(c echo.Context, config *auth.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 auth.CognitoConfig) (*TokenResponse, error) {
func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config auth.ConfigProvider) (*TokenResponse, error) {
data := url.Values{}
data.Set("grant_type", "authorization_code")
data.Set("client_id", config.AuthClientID)
data.Set("client_id", config.GetAuthClientID())
data.Set("code", authCode)
data.Set("redirect_uri", config.AuthRedirectURI)
data.Set("redirect_uri", config.GetAuthRedirectURI())
data.Set("code_verifier", codeVerifier) // Include code verifier for PKCE
config.AuthLogger.Debug("Token request details",
"url", config.AuthTokenURL,
"client_id", config.AuthClientID,
"redirect_uri", config.AuthRedirectURI)
config.GetAuthLogger().Debug("Token request details",
"url", config.GetAuthTokenURL(),
"client_id", config.GetAuthClientID(),
"redirect_uri", config.GetAuthRedirectURI())
req, err := http.NewRequest("POST", config.AuthTokenURL, strings.NewReader(data.Encode()))
req, err := http.NewRequest("POST", config.GetAuthTokenURL(), strings.NewReader(data.Encode()))
if err != nil {
return nil, fmt.Errorf("failed to create token request: %w", err)
}
@@ -87,11 +87,11 @@ func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config auth.Co
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// Add Authorization header if client secret is provided
if config.AuthClientSecret != "" {
req.SetBasicAuth(config.AuthClientID, config.AuthClientSecret)
config.AuthLogger.Debug("Using Basic Auth authentication")
if config.GetAuthClientSecret() != "" {
req.SetBasicAuth(config.GetAuthClientID(), config.GetAuthClientSecret())
config.GetAuthLogger().Debug("Using Basic Auth authentication")
} else {
config.AuthLogger.Debug("Using public client authentication (no secret)")
config.GetAuthLogger().Debug("Using public client authentication (no secret)")
}
client := &http.Client{Timeout: 10 * time.Second}
@@ -121,7 +121,7 @@ func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config auth.Co
// 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 *auth.CognitoConfig) error {
func handleOAuthCallback(c echo.Context, config auth.ConfigProvider) error {
// Extract the authorization code and state
code := c.QueryParam("code")
if code == "" {
@@ -144,52 +144,52 @@ func handleOAuthCallback(c echo.Context, config *auth.CognitoConfig) error {
}
// Retrieve stored code verifier
codeVerifier, err := getCodeVerifier(state, config.AuthLogger)
codeVerifier, err := getCodeVerifier(state, config.GetAuthLogger())
if err != nil {
config.AuthLogger.Error("Failed to retrieve code verifier", "error", err, "state", state)
config.GetAuthLogger().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)
tokens, err := exchangeCodeForTokensWithPKCE(code, codeVerifier, config)
if err != nil {
config.AuthLogger.Error("Failed to exchange code for tokens", "error", err)
config.GetAuthLogger().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.AuthJwksURL, config.AuthLogger)
keySet, err := GetJWKS(config.GetAuthJwksURL(), config.GetAuthLogger())
if err != nil {
config.AuthLogger.Error("Failed to fetch JWKS", "error", err)
config.GetAuthLogger().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)
idTokenClaims, err := verifyToken(tokens.IDToken, keySet, config)
if err != nil {
config.AuthLogger.Error("Failed to verify ID token", "error", err)
config.GetAuthLogger().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.AuthLogger.Warn("Failed to extract user groups", "error", err)
config.GetAuthLogger().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.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)
config.GetAuthLogger().Info("ID Token Claims", "claims", idTokenClaims)
config.GetAuthLogger().Info("Raw ID Token", "token", tokens.IDToken)
config.GetAuthLogger().Info("Raw Access Token", "token", tokens.AccessToken)
}
// Check authorization
authorized, requiredGroups := checkPermissions(config.AuthCallbackPath, userGroups, config.AuthRoutePermissions, config.AuthLogger)
authorized, requiredGroups := checkPermissions(config.GetAuthCallbackPath(), userGroups, config.GetAuthRoutePermissions(), config.GetAuthLogger())
if !authorized {
config.AuthLogger.Warn("OAuth callback - access denied",
config.GetAuthLogger().Warn("OAuth callback - access denied",
"user", idTokenClaims["cognito:username"],
"groups", userGroups,
"required", requiredGroups)
@@ -224,14 +224,14 @@ func handleOAuthCallback(c echo.Context, config *auth.CognitoConfig) error {
// Extract username for display purposes (optional)
username, _ := idTokenClaims["cognito:username"].(string)
config.AuthLogger.Info("User authenticated successfully", "username", username, "groups", userGroups)
config.GetAuthLogger().Info("User authenticated successfully", "username", username, "groups", userGroups)
// Redirect to home page
return c.Redirect(http.StatusFound, config.AuthHomePath)
return c.Redirect(http.StatusFound, config.GetAuthHomePath())
}
// LogoutHandler handles the logout process by clearing the auth cookie
func LogoutHandler(c echo.Context, config *auth.CognitoConfig) error {
func LogoutHandler(c echo.Context, config auth.ConfigProvider) error {
// Clear the token cookie
cookie := new(http.Cookie)
cookie.Name = "auth_token"
@@ -242,5 +242,5 @@ func LogoutHandler(c echo.Context, config *auth.CognitoConfig) error {
c.SetCookie(cookie)
// Redirect to home page
return c.Redirect(http.StatusFound, config.AuthHomePath)
return c.Redirect(http.StatusFound, config.GetAuthHomePath())
}