diff --git a/cmd/cognito_test/cognitotest/readme.md b/cmd/cognito_test/cognitotest/readme.md new file mode 100644 index 00000000..a8532dda --- /dev/null +++ b/cmd/cognito_test/cognitotest/readme.md @@ -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. \ No newline at end of file