This commit is contained in:
jay brown
2025-03-20 15:36:08 -07:00
parent dda6d7ee8c
commit 99e1a51b53
+198
View File
@@ -0,0 +1,198 @@
# Cognito harness for auth tests
The code in main.go is a harness for running the auth tests.
To test it set the following environment variables:
```json
export COGNITO_CLIENT_ID="552cqkf3640t39ncehkmgpce31";
export COGNITO_CLIENT_SECRET="omitted";
export COGNITO_DOMAIN="us-east-21y6po8rr8.auth.us-east-2.amazoncognito.com";
export COGNITO_USER_POOL_ID="us-east-2_1y6po8rR8";export AWS_REGION="us-east-2"
```
Run the code with `go run main.go` with these ^ environment variables set.
Try to login to the user group login test page (this will redirect to localhost:8080/query)
https://us-east-21y6po8rr8.auth.us-east-2.amazoncognito.com/login?client_id=552cqkf3640t39ncehkmgpce31&response_type=code&scope=email+openid+phone&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fquery
If it works you should see a response like this:
```json
{"authenticated":true,"email":"betot75403@isorax.com","groups":["uploaders","querybuilders"],"username":"testuser"}
```
Now that this code is verified it will be extracted and moved into an echo middleware that will handle the auth for the API.
# AWS Cognito Authentication Flow with Localhost - explainer for the code
This rest of this document explains the step-by-step flow that occurs when a user authenticates through AWS Cognito and gets redirected to your localhost server
in this test harness.
## Prerequisites
- You have an AWS Cognito User Pool set up
- You have a test user created in this pool
- Your Cognito app client is configured with `http://localhost:8080/query` as an allowed callback URL
- The provided Go server is running on your local machine
## Authentication Flow
### 1. Initial Authentication Request
When a user wants to authenticate, they're first directed to the Cognito hosted UI. This happens outside of the code you provided, typically through a link like:
```
https://{domain}.auth.{region}.amazoncognito.com/login?client_id={clientId}&response_type=code&scope=email+openid+profile&redirect_uri=http://localhost:8080/query
```
Where:
- `{domain}` is your Cognito domain (from the `COGNITO_DOMAIN` environment variable)
- `{region}` is your AWS region (from the `AWS_REGION` environment variable)
- `{clientId}` is your app client ID (from the `COGNITO_CLIENT_ID` environment variable)
### 2. User Login
The user logs in with their username and password on the Cognito hosted UI.
### 3. Redirect to Localhost
After successful authentication, Cognito redirects the user to the specified callback URL:
```
http://localhost:8080/query?code={authorization_code}
```
The authorization code is a temporary token that your application can exchange for actual access tokens.
### 4. Server Handles the Callback
When the user's browser loads the redirect URL, your Go server processes the request through the `HandleCallback` function:
1. **Extract Authorization Code**:
```go
code := r.URL.Query().Get("code")
```
2. **Check for Errors**:
The function checks if Cognito returned any error messages.
3. **Get Configuration**:
It retrieves your Cognito User Pool ID and AWS region from environment variables or falls back to hardcoded values.
4. **Fetch JWKS (JSON Web Key Set)**:
```go
jwksURL := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", region, userPoolID)
```
The function fetches the JWKS, which contains the public keys needed to verify the JWT tokens that Cognito issues.
### 5. Exchange Authorization Code for Tokens
The `VerifyCognitoCode` function handles the token exchange:
1. **Prepare Request to Token Endpoint**:
It creates a request to the Cognito token endpoint with:
- `grant_type=authorization_code`
- Your client ID
- The authorization code
- Your redirect URI
2. **Add Authentication**:
If you have a client secret, it adds Basic Authentication.
3. **Send the Request**:
It sends the request to Cognito's token endpoint:
```
https://{domain}.auth.{region}.amazoncognito.com/oauth2/token
```
4. **Parse the Response**:
Cognito returns a response containing:
- ID token (contains user information)
- Access token (for API access)
- Refresh token (for getting new tokens)
- Token expiration time
### 6. Verify the ID Token
The `verifyToken` function verifies the authenticity of the ID token:
1. **Determine the Issuer**:
```go
issuer := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s", region, config.UserPoolID)
```
2. **Verify Token Signature**:
It uses the JWKS (fetched earlier) to verify the token's cryptographic signature.
3. **Extract Claims**:
After verification, it extracts all the claims (user information) from the token.
### 7. Extract User Groups
The `GetUserGroups` function attempts to extract the user's group memberships from the token claims:
1. **Check Multiple Possible Fields**:
```go
possibleGroupFields := []string{"cognito:groups", "groups", "custom:groups"}
```
It looks for group information in various possible claim names.
2. **Handle Different Data Types**:
It handles cases where groups might be represented as arrays, strings, or other formats.
### 8. Return Response to User
Finally, the server returns a JSON response to the user's browser:
```json
{
"authenticated": true,
"username": "user123",
"email": "user@example.com",
"groups": ["admin", "users"]
}
```
This confirms successful authentication and provides basic user information.
## Debugging Information
Throughout this process, the server outputs various debugging information:
- Configuration details (client ID, domain, token URL, etc.)
- Token request details
- Authentication method (Basic Auth or public client)
- JWKS URL
- Issuer URL
This information helps in troubleshooting any authentication issues.
## Security Features
The implementation includes several security features:
1. **Token Verification**:
It cryptographically verifies the tokens using the JWKS.
2. **URL Validation**:
It validates the JWKS URL to prevent potential security issues.
3. **Timeouts**:
HTTP requests use timeouts to prevent hanging connections.
4. **Server Timeouts**:
The HTTP server uses timeouts to protect against slow client attacks.
5. **Masking Sensitive Values**:
The `maskString` function masks sensitive values in logs.
## Error Handling
The server handles various error scenarios:
- Missing authorization code
- Authorization errors from Cognito
- Failed token requests
- Invalid or expired tokens
- Missing user groups
Each error is properly logged and an appropriate HTTP status code is returned.