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
@@ -6,6 +6,8 @@ import (
"os"
"time"
"queryorchestration/internal/serviceconfig/auth"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
@@ -34,7 +36,7 @@ func main() {
logger := slog.New(logHandler)
// Create auth config from environment variables
config := cognitoauth.NewConfigFromEnv("http://localhost:8080", logger)
config := auth.InitializeConfig("http://localhost:8080", logger)
// Set route permissions
routePermissions := map[string][]string{
+14 -11
View File
@@ -4,6 +4,9 @@ import (
"context"
"fmt"
"net/http"
"queryorchestration/internal/serviceconfig/auth"
"strings"
"github.com/labstack/echo/v4"
@@ -11,15 +14,15 @@ import (
)
// RegisterRoutes registers all authentication-related routes to the Echo engine
func RegisterRoutes(e *echo.Echo, config *CognitoConfig) {
func RegisterRoutes(e *echo.Echo, config *auth.CognitoConfig) {
// Register the login route - the middleware will handle this
e.GET(config.LoginPath, func(c echo.Context) error {
e.GET(config.AuthLoginPath, func(c echo.Context) error {
// This is handled by the middleware
return nil
})
// Register callback route - the middleware will handle this
e.GET(config.CallbackPath, func(c echo.Context) error {
e.GET(config.AuthCallbackPath, func(c echo.Context) error {
// This handler will only be called for requests that don't have a code parameter
// (those are handled directly in the middleware)
@@ -36,12 +39,12 @@ func RegisterRoutes(e *echo.Echo, config *CognitoConfig) {
})
// Register logout route
e.GET(config.LogoutPath, func(c echo.Context) error {
e.GET(config.AuthLogoutPath, func(c echo.Context) error {
return LogoutHandler(c, config)
})
// Default home page handler if requested
e.GET(config.HomePath, func(c echo.Context) error {
e.GET(config.AuthHomePath, func(c echo.Context) error {
return HomeHandler(c, config)
})
@@ -53,20 +56,20 @@ func RegisterRoutes(e *echo.Echo, config *CognitoConfig) {
}
// HomeHandler implements a simple home page that shows auth status and available endpoints
func HomeHandler(c echo.Context, config *CognitoConfig) error {
func HomeHandler(c echo.Context, config *auth.CognitoConfig) error {
// Create a list of all registered routes
var endpoints []string
// Add the auth routes
endpoints = append(endpoints, []string{
config.LoginPath,
config.CallbackPath,
config.HomePath,
config.LogoutPath,
config.AuthLoginPath,
config.AuthCallbackPath,
config.AuthHomePath,
config.AuthLogoutPath,
}...)
// Add routes from the permission map
for route := range config.RoutePermissions {
for route := range config.AuthRoutePermissions {
// Skip routes that are already in the list
alreadyAdded := false
for _, endpoint := range endpoints {
-146
View File
@@ -1,146 +0,0 @@
package cognitoauth
import (
"fmt"
"log"
"log/slog"
"os"
"strings"
)
// CognitoConfig holds the configuration for AWS Cognito
// Contains all necessary parameters to interact with Cognito endpoints
type CognitoConfig struct {
RedirectURI string // URL where Cognito redirects after authentication
TokenURL string // Cognito endpoint for token operations
JwksURL string // URL for JSON Web Key Set (for token verification)
AuthURL string // Cognito authorization endpoint for initiating login
LoginPath string // Path for initiating login
CallbackPath string // Path for OAuth callback
HomePath string // Path for home page
LogoutPath string // Path for logout
Logger *slog.Logger // Logger instance
RoutePermissions map[string][]string // Map of routes to required permissions
// annotated vars
Region string `env:"COGNITO_REGION" envDefault:"us-east-2"`
UserPoolID string `env:"COGNITO_USER_POOL_ID" required:"true"`
ClientSecret string `env:"COGNITO_CLIENT_SECRET" required:"true"`
Domain string `env:"COGNITO_DOMAIN" required:"true"`
ClientID string `env:"COGNITO_CLIENT_ID" required:"true"`
}
// NewConfigFromEnv creates a new CognitoConfig from environment variables
func NewConfigFromEnv(baseURL string, logger *slog.Logger) *CognitoConfig {
// Set default logger if not provided
if logger == nil {
var logLevel slog.Level
if os.Getenv("DEBUG") == "true" {
logLevel = slog.LevelDebug
} else {
logLevel = slog.LevelInfo
}
logHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: logLevel,
})
logger = slog.New(logHandler)
}
// Set paths with defaults
loginPath := os.Getenv("COGNITO_LOGIN_PATH")
if loginPath == "" {
loginPath = "/login"
}
callbackPath := os.Getenv("COGNITO_CALLBACK_PATH")
if callbackPath == "" {
callbackPath = "/login-callback"
}
homePath := os.Getenv("COGNITO_HOME_PATH")
if homePath == "" {
homePath = "/home"
}
logoutPath := os.Getenv("COGNITO_LOGOUT_PATH")
if logoutPath == "" {
logoutPath = "/logout"
}
// Get AWS region with fallback
region := os.Getenv("AWS_REGION")
if region == "" {
region = "us-east-2" // Fallback to default value
}
// Get User Pool ID with fallback
userPoolID := os.Getenv("COGNITO_USER_POOL_ID")
if userPoolID == "" {
userPoolID = "" // No default, will need to be provided
logger.Warn("COGNITO_USER_POOL_ID environment variable is not set")
}
// Get Cognito domain
domain := os.Getenv("COGNITO_DOMAIN")
if domain == "" {
//logger.Warn("COGNITO_DOMAIN environment variable is not set, using default")
//// Use the user pool ID directly as part of the domain
//domain = fmt.Sprintf("%s.auth.%s.amazoncognito.com", userPoolID, region)
log.Fatalf("COGNITO_DOMAIN environment variable is not set")
}
// Ensure domain format is correct (no protocol prefix)
domain = strings.TrimPrefix(domain, "https://")
domain = strings.TrimPrefix(domain, "http://")
// Sanitize base URL (remove trailing slash)
baseURL = strings.TrimSuffix(baseURL, "/")
config := &CognitoConfig{
ClientID: os.Getenv("COGNITO_CLIENT_ID"),
ClientSecret: os.Getenv("COGNITO_CLIENT_SECRET"),
RedirectURI: baseURL + callbackPath,
TokenURL: fmt.Sprintf("https://%s/oauth2/token", domain),
AuthURL: fmt.Sprintf("https://%s/oauth2/authorize", domain),
JwksURL: fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", region, userPoolID),
UserPoolID: userPoolID,
Region: region,
LoginPath: loginPath,
CallbackPath: callbackPath,
HomePath: homePath,
LogoutPath: logoutPath,
Logger: logger,
RoutePermissions: make(map[string][]string),
}
config.PrettyPrint()
return config
}
func (c *CognitoConfig) PrettyPrint() {
fmt.Printf("CognitoConfig:\n")
fmt.Printf(" ClientID: %s\n", c.ClientID)
fmt.Printf(" ClientSecret: %s\n", c.ClientSecret)
fmt.Printf(" RedirectURI: %s\n", c.RedirectURI)
fmt.Printf(" TokenURL: %s\n", c.TokenURL)
fmt.Printf(" AuthURL: %s\n", c.AuthURL)
fmt.Printf(" JwksURL: %s\n", c.JwksURL)
fmt.Printf(" UserPoolID: %s\n", c.UserPoolID)
fmt.Printf(" Region: %s\n", c.Region)
fmt.Printf(" LoginPath: %s\n", c.LoginPath)
fmt.Printf(" CallbackPath: %s\n", c.CallbackPath)
fmt.Printf(" HomePath: %s\n", c.HomePath)
fmt.Printf(" LogoutPath: %s\n", c.LogoutPath)
// pretty print the route permissions map
for route, permissions := range c.RoutePermissions {
fmt.Printf(" Route: %s, Permissions: %v\n", route, permissions)
}
}
// SetRoutePermissions sets the route permissions map
func (c *CognitoConfig) SetRoutePermissions(permissions map[string][]string) {
c.RoutePermissions = permissions
}
+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)
}
+21 -18
View File
@@ -2,6 +2,9 @@ package cognitoauth
import (
"net/http"
"queryorchestration/internal/serviceconfig/auth"
"strings"
"github.com/labstack/echo/v4"
@@ -10,24 +13,24 @@ import (
// TokenValidationMiddleware verifies JWT tokens efficiently
// This is the primary middleware that handles both authentication and authorization
// Applied to all routes to enforce security requirements
func TokenValidationMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
func TokenValidationMiddleware(config *auth.CognitoConfig) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
requestPath := c.Request().URL.Path
config.Logger.Debug("Processing request", "path", requestPath, "method", c.Request().Method)
config.AuthLogger.Debug("Processing request", "path", requestPath, "method", c.Request().Method)
// Skip token validation for specified public paths
publicPaths := []string{config.HomePath, config.LogoutPath}
publicPaths := []string{config.AuthHomePath, config.AuthLogoutPath}
for _, path := range publicPaths {
if requestPath == path {
config.Logger.Debug("Skipping token validation for public path", "path", requestPath)
config.AuthLogger.Debug("Skipping token validation for public path", "path", requestPath)
return next(c)
}
}
// Special case for the OAuth callback path
if requestPath == config.CallbackPath && (c.QueryParam("code") != "" || c.QueryParam("error") != "") {
config.Logger.Debug("Handling OAuth callback",
if requestPath == config.AuthCallbackPath && (c.QueryParam("code") != "" || c.QueryParam("error") != "") {
config.AuthLogger.Debug("Handling OAuth callback",
"has_code", c.QueryParam("code") != "",
"has_error", c.QueryParam("error") != "")
// Skip token validation for OAuth callback - it will be handled by the callback handler
@@ -35,14 +38,14 @@ func TokenValidationMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
}
// Initialize login flow if this is a login request
if requestPath == config.LoginPath {
if requestPath == config.AuthLoginPath {
return initiateLoginWithPKCE(c, config)
}
// Get authorization header
authHeader := c.Request().Header.Get("Authorization")
if authHeader == "" {
config.Logger.Warn("No authorization header provided")
config.AuthLogger.Warn("No authorization header provided")
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Authorization required"})
}
@@ -53,23 +56,23 @@ func TokenValidationMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
}
// Get JWKS (cached if possible)
keySet, err := GetJWKS(config.JwksURL, config.Logger)
keySet, err := GetJWKS(config.AuthJwksURL, config.AuthLogger)
if err != nil {
config.Logger.Error("Failed to get JWKS", "error", err)
config.AuthLogger.Error("Failed to get JWKS", "error", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to verify token"})
}
// Verify token
claims, err := verifyToken(tokenStr, keySet, *config)
if err != nil {
config.Logger.Warn("Token verification failed", "error", err)
config.AuthLogger.Warn("Token verification failed", "error", err)
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Invalid token"})
}
// Extract user groups
userGroups, err := GetUserGroups(claims)
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{} // Empty array if no groups found
}
@@ -82,9 +85,9 @@ func TokenValidationMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
c.Set("user_info", userInfo)
// Check authorization
authorized, requiredGroups := checkPermissions(requestPath, userGroups, config.RoutePermissions, config.Logger)
authorized, requiredGroups := checkPermissions(requestPath, userGroups, config.AuthRoutePermissions, config.AuthLogger)
if !authorized {
config.Logger.Warn("Access denied",
config.AuthLogger.Warn("Access denied",
"path", requestPath,
"user", claims["cognito:username"],
"groups", userGroups,
@@ -106,16 +109,16 @@ func TokenValidationMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
}
// JWTAuthMiddleware checks for JWT token in cookies and adds it to the Authorization header
func JWTAuthMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
func JWTAuthMiddleware(config *auth.CognitoConfig) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Skip for login and callback paths
if c.Path() == config.LoginPath || c.Path() == config.CallbackPath {
if c.Path() == config.AuthLoginPath || c.Path() == config.AuthCallbackPath {
return next(c)
}
// Skip for home and logout as they should be accessible without auth
if c.Path() == config.HomePath || c.Path() == config.LogoutPath {
if c.Path() == config.AuthHomePath || c.Path() == config.AuthLogoutPath {
return next(c)
}
@@ -130,7 +133,7 @@ func JWTAuthMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
tokenCookie, err := c.Cookie("auth_token")
if err != nil || tokenCookie.Value == "" {
// No token cookie found
return c.Redirect(http.StatusFound, config.LoginPath)
return c.Redirect(http.StatusFound, config.AuthLoginPath)
}
// Add token to Authorization header
+5 -3
View File
@@ -10,6 +10,8 @@ import (
"log/slog"
"time"
"queryorchestration/internal/serviceconfig/auth"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
)
@@ -17,9 +19,9 @@ import (
// verifyToken verifies the JWT token and returns its claims
// Used during both OAuth callback and subsequent API requests with bearer token
// Verifies signature, expiration, issuer, and other JWT claims
func verifyToken(tokenString string, keySet jwk.Set, config CognitoConfig) (map[string]interface{}, error) {
region := config.Region
issuer := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s", region, config.UserPoolID)
func verifyToken(tokenString string, keySet jwk.Set, config auth.CognitoConfig) (map[string]interface{}, error) {
region := config.AuthRegion
issuer := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s", region, config.AuthUserPoolID)
// Verify the token with the keySet
verifiedToken, err := jwt.Parse(
+1 -1
View File
@@ -138,7 +138,7 @@ func (ckp *CognitoKeyProvider) GetPublicKey(kid string) (*rsa.PublicKey, error)
// Fetch JWKS from Cognito (old way)
// baseURL := "https://cognito-idp.amazonaws.com"
// jwksPath := fmt.Sprintf("/%s/%s/.well-known/jwks.json", ckp.Region, ckp.UserPoolID)
// jwksPath := fmt.Sprintf("/%s/%s/.well-known/jwks.json", ckp.AuthRegion, ckp.AuthUserPoolID)
// https://cognito-idp.us-east-2.amazonaws.com/us-east-2_1y6po8rR8/.well-known/jwks.json
jwksURL := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", ckp.Region, ckp.UserPoolID)
+4 -4
View File
@@ -533,8 +533,8 @@ func TestCognitoKeyProviderGetPublicKey(t *testing.T) {
// Instead of trying to patch the URL (which is difficult with reflection),
// we'll verify that the provider was created correctly
regionField := reflect.Indirect(providerValue).FieldByName("Region")
userPoolIDField := reflect.Indirect(providerValue).FieldByName("UserPoolID")
regionField := reflect.Indirect(providerValue).FieldByName("AuthRegion")
userPoolIDField := reflect.Indirect(providerValue).FieldByName("AuthUserPoolID")
assert.Equal(t, "us-east-1", regionField.String())
assert.Equal(t, "test-pool-id", userPoolIDField.String())
@@ -552,10 +552,10 @@ func TestNewCognitoKeyProvider(t *testing.T) {
// Use reflection to check the internal fields
providerValue := reflect.ValueOf(provider).Elem()
regionField := providerValue.FieldByName("Region")
regionField := providerValue.FieldByName("AuthRegion")
assert.Equal(t, region, regionField.String())
userPoolIDField := providerValue.FieldByName("UserPoolID")
userPoolIDField := providerValue.FieldByName("AuthUserPoolID")
assert.Equal(t, userPoolID, userPoolIDField.String())
jwksCacheField := providerValue.FieldByName("JwksCache")
@@ -1,4 +1,4 @@
package cognitoauth
package auth
import (
"fmt"
@@ -12,22 +12,22 @@ import (
// Contains all necessary parameters to interact with Cognito endpoints
type CognitoConfig struct {
// annotated vars
Region string `env:"COGNITO_REGION" envDefault:"us-east-2"`
UserPoolID string `env:"COGNITO_USER_POOL_ID,required,notEmpty"`
ClientSecret string `env:"COGNITO_CLIENT_SECRET,required,notEmpty"`
Domain string `env:"COGNITO_DOMAIN,required,notEmpty"`
ClientID string `env:"COGNITO_CLIENT_ID,required,notEmpty"`
AuthRegion string `env:"COGNITO_REGION" envDefault:"us-east-2"`
AuthUserPoolID string `env:"COGNITO_USER_POOL_ID,required,notEmpty"`
AuthClientSecret string `env:"COGNITO_CLIENT_SECRET,required,notEmpty"`
AuthDomain string `env:"COGNITO_DOMAIN,required,notEmpty"`
AuthClientID string `env:"COGNITO_CLIENT_ID,required,notEmpty"`
RedirectURI string // URL where Cognito redirects after authentication
TokenURL string // Cognito endpoint for token operations
JwksURL string // URL for JSON Web Key Set (for token verification)
AuthURL string // Cognito authorization endpoint for initiating login
LoginPath string // Path for initiating login
CallbackPath string // Path for OAuth callback
HomePath string // Path for home page
LogoutPath string // Path for logout
AuthLogger *slog.Logger // Logger instance ( normally just the same logger as main)
RoutePermissions map[string][]string // Map of routes to required permissions
AuthRedirectURI string // URL where Cognito redirects after authentication
AuthTokenURL string // Cognito endpoint for token operations
AuthJwksURL string // URL for JSON Web Key Set (for token verification)
AuthURL string // Cognito authorization endpoint for initiating login
AuthLoginPath string // Path for initiating login
AuthCallbackPath string // Path for OAuth callback
AuthHomePath string // Path for home page
AuthLogoutPath string // Path for logout
AuthLogger *slog.Logger // Logger instance ( normally just the same logger as main)
AuthRoutePermissions map[string][]string // Map of routes to required permissions
}
// InitializeConfig creates a new CognitoConfig from environment variables
@@ -99,20 +99,20 @@ func InitializeConfig(baseURL string, logger *slog.Logger) *CognitoConfig {
baseURL = strings.TrimSuffix(baseURL, "/")
config := &CognitoConfig{
ClientID: os.Getenv("COGNITO_CLIENT_ID"),
ClientSecret: os.Getenv("COGNITO_CLIENT_SECRET"),
RedirectURI: baseURL + callbackPath,
TokenURL: fmt.Sprintf("https://%s/oauth2/token", domain),
AuthURL: fmt.Sprintf("https://%s/oauth2/authorize", domain),
JwksURL: fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", region, userPoolID),
UserPoolID: userPoolID,
Region: region,
LoginPath: loginPath,
CallbackPath: callbackPath,
HomePath: homePath,
LogoutPath: logoutPath,
AuthLogger: logger,
RoutePermissions: make(map[string][]string),
AuthClientID: os.Getenv("COGNITO_CLIENT_ID"),
AuthClientSecret: os.Getenv("COGNITO_CLIENT_SECRET"),
AuthRedirectURI: baseURL + callbackPath,
AuthTokenURL: fmt.Sprintf("https://%s/oauth2/token", domain),
AuthURL: fmt.Sprintf("https://%s/oauth2/authorize", domain),
AuthJwksURL: fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", region, userPoolID),
AuthUserPoolID: userPoolID,
AuthRegion: region,
AuthLoginPath: loginPath,
AuthCallbackPath: callbackPath,
AuthHomePath: homePath,
AuthLogoutPath: logoutPath,
AuthLogger: logger,
AuthRoutePermissions: make(map[string][]string),
}
config.PrettyPrint()
@@ -121,21 +121,21 @@ func InitializeConfig(baseURL string, logger *slog.Logger) *CognitoConfig {
func (c *CognitoConfig) PrettyPrint() {
fmt.Printf("CognitoConfig:\n")
fmt.Printf(" ClientID: %s\n", c.ClientID)
fmt.Printf(" ClientSecret: %s\n", c.ClientSecret)
fmt.Printf(" RedirectURI: %s\n", c.RedirectURI)
fmt.Printf(" TokenURL: %s\n", c.TokenURL)
fmt.Printf(" AuthClientID: %s\n", c.AuthClientID)
fmt.Printf(" AuthClientSecret: %s\n", c.AuthClientSecret)
fmt.Printf(" AuthRedirectURI: %s\n", c.AuthRedirectURI)
fmt.Printf(" AuthTokenURL: %s\n", c.AuthTokenURL)
fmt.Printf(" AuthURL: %s\n", c.AuthURL)
fmt.Printf(" JwksURL: %s\n", c.JwksURL)
fmt.Printf(" UserPoolID: %s\n", c.UserPoolID)
fmt.Printf(" Region: %s\n", c.Region)
fmt.Printf(" LoginPath: %s\n", c.LoginPath)
fmt.Printf(" CallbackPath: %s\n", c.CallbackPath)
fmt.Printf(" HomePath: %s\n", c.HomePath)
fmt.Printf(" LogoutPath: %s\n", c.LogoutPath)
fmt.Printf(" AuthJwksURL: %s\n", c.AuthJwksURL)
fmt.Printf(" AuthUserPoolID: %s\n", c.AuthUserPoolID)
fmt.Printf(" AuthRegion: %s\n", c.AuthRegion)
fmt.Printf(" AuthLoginPath: %s\n", c.AuthLoginPath)
fmt.Printf(" AuthCallbackPath: %s\n", c.AuthCallbackPath)
fmt.Printf(" AuthHomePath: %s\n", c.AuthHomePath)
fmt.Printf(" AuthLogoutPath: %s\n", c.AuthLogoutPath)
// pretty print the route permissions map
for route, permissions := range c.RoutePermissions {
for route, permissions := range c.AuthRoutePermissions {
fmt.Printf(" Route: %s, Permissions: %v\n", route, permissions)
}
@@ -143,5 +143,5 @@ func (c *CognitoConfig) PrettyPrint() {
// SetRoutePermissions sets the route permissions map
func (c *CognitoConfig) SetRoutePermissions(permissions map[string][]string) {
c.RoutePermissions = permissions
c.AuthRoutePermissions = permissions
}
+4 -2
View File
@@ -4,7 +4,9 @@ import (
"errors"
"fmt"
"log/slog"
"queryorchestration/internal/serviceconfig/cognitoauth"
"queryorchestration/internal/serviceconfig/auth"
"reflect"
"strings"
"sync"
@@ -33,7 +35,7 @@ type BaseConfig struct {
Pwd string `env:"PWD,required,notEmpty"`
//rbac.AuthConfig
cognitoauth.CognitoConfig
auth.CognitoConfig
logger.LogConfig
observability.ObsConfig
database.DBConfig