206 lines
11 KiB
Markdown
206 lines
11 KiB
Markdown
# AWS Cognito PKCE Authentication Example
|
|
This directory contains a test harness for validating AWS Cognito PKCE (Proof Key for Code Exchange) authentication
|
|
and authorization with Go and the Echo web framework. It demonstrates a complete OAuth 2.0 flow with
|
|
role-based access control (RBAC).
|
|
It is temporary and will go away once the final middleware is in place.
|
|
|
|
## What is PKCE?
|
|
|
|
PKCE (Proof Key for Code Exchange) is an extension to the OAuth 2.0 authorization code flow designed to prevent authorization code interception attacks. It's particularly important for:
|
|
|
|
- Public clients (like SPAs and mobile apps)
|
|
- Applications where securely storing a client secret is difficult
|
|
- Applications that run in potentially untrusted environments
|
|
|
|
PKCE works by using a dynamically created cryptographic challenge derived from a secret code verifier. This prevents attackers from exchanging an intercepted authorization code for tokens without possessing the original code verifier.
|
|
|
|
## How PKCE Works in This Application
|
|
|
|
1. **Code Verifier Generation**: When a user initiates login, the application generates a random string (code verifier).
|
|
2. **Code Challenge Creation**: The application derives a code challenge from the verifier using SHA-256 hashing and Base64URL encoding.
|
|
3. **Authorization Request**: The application redirects to Cognito with the code challenge.
|
|
4. **User Authentication**: The user authenticates with Cognito.
|
|
5. **Authorization Code**: Cognito redirects back to the application with an authorization code.
|
|
6. **Token Exchange**: The application exchanges the code and original code verifier for access and ID tokens.
|
|
|
|
## Prerequisites
|
|
|
|
- Go 1.16+
|
|
- AWS account with Cognito User Pool
|
|
- Properly configured Cognito App Client (see setup section)
|
|
|
|
## Dependencies
|
|
|
|
- [Echo](https://github.com/labstack/echo) (v4) - Web framework
|
|
- [JWKS](https://github.com/lestrrat-go/jwx) - JSON Web Key Set handling for token verification
|
|
|
|
## Cognito App Client Requirements
|
|
|
|
Your Cognito App Client must be configured as follows:
|
|
|
|
1. **No Client Secret**: The app client should be configured as a public client without a client secret.
|
|
2. **Allowed OAuth Flows**: Authorization code grant
|
|
3. **Allowed OAuth Scopes**: `openid`, `email`, `profile` (and optionally `phone`)
|
|
4. **Callback URL**: Must include `http://localhost:8080/query` (or your custom endpoint)
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Description | Example | Required |
|
|
|----------|-------------|---------|----------|
|
|
| `COGNITO_CLIENT_ID` | Your Cognito App Client ID | `6u3ppajoksbkdlteacg0ukbnfi` | Yes |
|
|
| `COGNITO_USER_POOL_ID` | Your Cognito User Pool ID | `us-east-2_1y6po8rR8` | Yes |
|
|
| `AWS_REGION` | AWS Region of your Cognito User Pool | `us-east-2` | Yes |
|
|
| `COGNITO_DOMAIN` | Your Cognito domain | `your-domain.auth.us-east-2.amazoncognito.com` | No (falls back to default) |
|
|
| `DEBUG` | Enable debug logging | `true` | No (defaults to false) |
|
|
|
|
## Creating a Compatible Cognito App Client
|
|
|
|
You can create a compatible app client with the AWS CLI:
|
|
|
|
# Creating a cognito app client for cognito pkce /login
|
|
Assumed the cognito user pool is already created.
|
|
```
|
|
aws --profile aarete --region us-east-2 --endpoint-url https://cognito-idp.us-east-2.amazonaws.com cognito-idp create-user-pool-client \
|
|
--user-pool-id us-east-2_1y6po8rR8 \
|
|
--client-name "public-pkce-client" \
|
|
--no-generate-secret \
|
|
--refresh-token-validity 5 \
|
|
--access-token-validity 60 \
|
|
--id-token-validity 60 \
|
|
--token-validity-units "RefreshToken=days,IdToken=minutes,AccessToken=minutes" \
|
|
--explicit-auth-flows ALLOW_REFRESH_TOKEN_AUTH ALLOW_USER_AUTH ALLOW_USER_SRP_AUTH \
|
|
--supported-identity-providers COGNITO \
|
|
--callback-urls http://localhost:8080/query \
|
|
--allowed-o-auth-flows code \
|
|
--allowed-o-auth-scopes email openid phone profile \
|
|
--allowed-o-auth-flows-user-pool-client \
|
|
--prevent-user-existence-errors ENABLED
|
|
```
|
|
|
|
## Setup and Running
|
|
|
|
1.2 omitted
|
|
3. Set your environment variables:
|
|
```bash
|
|
export COGNITO_CLIENT_ID=<your-client-id>
|
|
export COGNITO_USER_POOL_ID=<your-user-pool-id>
|
|
export AWS_REGION=<your-region>
|
|
export DEBUG=true # Optional for debug logging
|
|
```
|
|
4. Run the application:
|
|
```bash
|
|
go run main.go
|
|
```
|
|
5. Visit `http://localhost:8080/login` in your browser to start the authentication flow
|
|
|
|
## Key Features
|
|
|
|
- **PKCE Implementation**: Secure authentication flow without client secrets
|
|
- **Token Validation**: JWT verification using Cognito's JWKS
|
|
- **Authorization**: Role-based access control using Cognito groups
|
|
- **Caching**: JWKS caching to minimize API calls
|
|
- **Token Handling**: Access and ID token management
|
|
- **Middleware Architecture**: Authentication and authorization via Echo middleware
|
|
|
|
## Authorization & Groups
|
|
|
|
This application implements role-based access control using Cognito user groups. Routes can be protected based on group membership:
|
|
|
|
```go
|
|
routePermissions := map[string][]string{
|
|
"/users": {"exporters"},
|
|
"/users/:id": {"exporters", "querybuilders"},
|
|
// Other routes and permissions
|
|
}
|
|
```
|
|
|
|
Users must belong to the required groups to access protected routes.
|
|
|
|
## Security Considerations
|
|
|
|
- The application uses in-memory state management for PKCE sessions. In production, use a persistent and scalable storage solution.
|
|
- The application has a 5-minute expiration for PKCE sessions.
|
|
- Proper error handling prevents information leakage.
|
|
- Token verification checks issuer, signature, and expiration.
|
|
|
|
## Flow Diagram
|
|
|
|
|
|
```
|
|
┌─────────┐ ┌───────────┐ ┌─────────┐
|
|
│ Browser │ │ App Server│ │ Cognito │
|
|
└────┬────┘ └─────┬─────┘ └────┬────┘
|
|
│ │ │
|
|
│ 1. GET /login │ │
|
|
│ ─────────────────────────────► │
|
|
│ │ │
|
|
│ │ 2. Generate code_verifier │
|
|
│ │ Generate code_challenge │
|
|
│ │ │
|
|
│ 3. Redirect to Cognito │ │
|
|
│ ◄───────────────────────────── │
|
|
│ │ │
|
|
│ 4. GET /oauth2/authorize?...code_challenge │
|
|
│ ────────────────────────────────────────────────────────────►
|
|
│ │ │
|
|
│ │ │ 5. Process auth
|
|
│ │ │ request
|
|
│ 6. Cognito Login Page │ │
|
|
│ ◄────────────────────────────────────────────────────────────
|
|
│ │ │
|
|
│ 7. User credentials │ │
|
|
│ ────────────────────────────────────────────────────────────►
|
|
│ │ │
|
|
│ │ │ 8. Validate
|
|
│ │ │ credentials
|
|
│ 9. Redirect with code │ │
|
|
│ ◄────────────────────────────────────────────────────────────
|
|
│ │ │
|
|
│ 10. GET /query?code=... │ │
|
|
│ ─────────────────────────────► │
|
|
│ │ │
|
|
│ │ 11. Retrieve code_verifier │
|
|
│ │ │
|
|
│ │ 12. POST /oauth2/token │
|
|
│ │ code=...&code_verifier=...
|
|
│ │ ─────────────────────────────►
|
|
│ │ │
|
|
│ │ │ 13. Validate
|
|
│ │ │ code and
|
|
│ │ │ verifier
|
|
│ │ 14. Tokens response │
|
|
│ │ ◄─────────────────────────────
|
|
│ │ │
|
|
│ │ 15. Verify tokens │
|
|
│ │ Check permissions │
|
|
│ │ │
|
|
│ 16. Authentication successful│ │
|
|
│ ◄───────────────────────────── │
|
|
│ │ │
|
|
|
|
```
|
|
|
|
## Mermaid Sequence Diagram
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Browser
|
|
participant AppServer as App Server
|
|
participant Cognito
|
|
|
|
Browser->>AppServer: 1. GET /login
|
|
Note over AppServer: 2. Generate code_verifier<br/>Generate code_challenge
|
|
AppServer->>Browser: 3. Redirect to Cognito
|
|
Browser->>Cognito: 4. GET /oauth2/authorize?...code_challenge
|
|
Note over Cognito: 5. Process auth request
|
|
Cognito->>Browser: 6. Cognito Login Page
|
|
Browser->>Cognito: 7. User credentials
|
|
Note over Cognito: 8. Validate credentials
|
|
Cognito->>Browser: 9. Redirect with code
|
|
Browser->>AppServer: 10. GET /query?code=...
|
|
Note over AppServer: 11. Retrieve code_verifier
|
|
AppServer->>Cognito: 12. POST /oauth2/token<br/>code=...&code_verifier=...
|
|
Note over Cognito: 13. Validate code and verifier
|
|
Cognito->>AppServer: 14. Tokens response
|
|
Note over AppServer: 15. Verify tokens<br/>Check permissions
|
|
AppServer->>Browser: 16. Authentication successful
|
|
``` |