基于AWS Cognito、API Gateway与Serverless的角色权限配置咨询
Hey there, let’s tackle your AWS permissions head-on. You’ve got three solid ideas, but the best approach is combining Cognito User Groups + IAM Roles (your first option) with a Pre-Token Generator Trigger (your third option). This leverages AWS’s native security tools, keeps maintenance low, and gives you both API-level and function-level control—perfect for your admin/regular user split.
Why This Combo Is Your Best Bet
- IAM Authorization is AWS’s battle-tested, native way to lock down API access. No custom token validation code means fewer security gaps and less maintenance overhead.
- Cognito User Groups simplify permission management: just add users to
AdminGrouporRegularUserGroupinstead of manually assigning roles to individual users. - Pre-Token Trigger injects user group info into your access/ID tokens, letting you add extra fine-grained checks in Lambda if needed (like restricting specific admin actions within an allowed API).
Comparing this to your other options:
- Custom authorizers work, but require writing and maintaining your own token validation logic—unnecessary when AWS’s native tools do this better.
- Using only a Pre-Token Trigger means you’d have to add permission checks to every Lambda function, which is error-prone and less efficient than blocking requests at the API Gateway level.
Full Implementation with Serverless Framework
Let’s build out the entire setup step by step.
Step 1: Serverless.yml Configuration
This defines all your AWS resources (Cognito, IAM roles, Lambdas, API Gateway) in one place.
service: aws-api-permission-control provider: name: aws runtime: nodejs18.x stage: dev region: us-east-1 iam: role: statements: # Allow Lambdas to access Cognito group data - Effect: Allow Action: cognito-idp:AdminListGroupsForUser Resource: !GetAtt UserPool.Arn functions: # API Lambda Functions getUser: handler: handlers/getUser.handler events: - http: path: user method: get authorizer: aws_iam # Enforce IAM authorization getVehicle: handler: handlers/getVehicle.handler events: - http: path: vehicle method: get authorizer: aws_iam postVehicle: handler: handlers/postVehicle.handler events: - http: path: vehicle method: post authorizer: aws_iam resources: Resources: # Cognito User Pool UserPool: Type: AWS::Cognito::UserPool Properties: UserPoolName: ${self:service}-user-pool-${self:provider.stage} UsernameAttributes: [email] AutoVerifiedAttributes: [email] Schema: - Name: email AttributeDataType: String Mutable: false Required: true # User Pool Client (for app authentication) UserPoolClient: Type: AWS::Cognito::UserPoolClient Properties: UserPoolId: !Ref UserPool ClientName: ${self:service}-client-${self:provider.stage} GenerateSecret: false ExplicitAuthFlows: - ALLOW_USER_PASSWORD_AUTH - ALLOW_REFRESH_TOKEN_AUTH # IAM Role for Admin Users (full API access) AdminIAMRole: Type: AWS::IAM::Role Properties: RoleName: ${self:service}-admin-role-${self:provider.stage} AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: { Federated: cognito-identity.amazonaws.com } Action: sts:AssumeRoleWithWebIdentity Condition: StringEquals: cognito-identity.amazonaws.com:aud: !Ref CognitoIdentityPool ForAnyValue:StringLike: cognito-identity.amazonaws.com:amr: authenticated Policies: - PolicyName: AdminAPIAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: execute-api:Invoke Resource: - !Sub "arn:aws:execute-api:${self:provider.region}:${AWS::AccountId}:${ApiGatewayRestApi}/*/GET/user" - !Sub "arn:aws:execute-api:${self:provider.region}:${AWS::AccountId}:${ApiGatewayRestApi}/*/GET/vehicle" - !Sub "arn:aws:execute-api:${self:provider.region}:${AWS::AccountId}:${ApiGatewayRestApi}/*/POST/vehicle" # IAM Role for Regular Users (only GET /vehicle access) RegularUserIAMRole: Type: AWS::IAM::Role Properties: RoleName: ${self:service}-regular-role-${self:provider.stage} AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: { Federated: cognito-identity.amazonaws.com } Action: sts:AssumeRoleWithWebIdentity Condition: StringEquals: cognito-identity.amazonaws.com:aud: !Ref CognitoIdentityPool ForAnyValue:StringLike: cognito-identity.amazonaws.com:amr: authenticated Policies: - PolicyName: RegularUserAPIAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: execute-api:Invoke Resource: - !Sub "arn:aws:execute-api:${self:provider.region}:${AWS::AccountId}:${ApiGatewayRestApi}/*/GET/vehicle" # Cognito Identity Pool (maps user groups to IAM roles) CognitoIdentityPool: Type: AWS::Cognito::IdentityPool Properties: IdentityPoolName: ${self:service}-identity-pool-${self:provider.stage} AllowUnauthenticatedIdentities: false CognitoIdentityProviders: - ClientId: !Ref UserPoolClient ProviderName: !GetAtt UserPool.ProviderName # Map User Groups to IAM Roles IdentityPoolRoleAttachment: Type: AWS::Cognito::IdentityPoolRoleAttachment Properties: IdentityPoolId: !Ref CognitoIdentityPool Roles: authenticated: !GetAtt RegularUserIAMRole.Arn # Default for authenticated users RoleMappings: AdminGroupMapping: Type: Token AmbiguousRoleResolution: AuthenticatedRole IdentityProvider: !Sub "cognito-idp.${self:provider.region}.amazonaws.com/${!Ref UserPool}" RulesConfiguration: Rules: - Claim: cognito:groups MatchType: Contains Value: AdminGroup RoleARN: !GetAtt AdminIAMRole.Arn # Cognito User Groups AdminGroup: Type: AWS::Cognito::UserPoolGroup Properties: GroupName: AdminGroup UserPoolId: !Ref UserPool Description: Full API access for admins RegularUserGroup: Type: AWS::Cognito::UserPoolGroup Properties: GroupName: RegularUserGroup UserPoolId: !Ref UserPool Description: Limited access for regular users # Pre-Token Generator Lambda (adds group info to tokens) PreTokenGeneratorLambda: Type: AWS::Lambda::Function Properties: FunctionName: ${self:service}-pre-token-${self:provider.stage} Runtime: nodejs18.x Handler: pre-token-generator.handler Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: | exports.handler = async (event) => { const { username, userPoolId } = event.request.userAttributes; const AWS = require('aws-sdk'); const cognito = new AWS.CognitoIdentityServiceProvider(); try { const groups = await cognito.adminListGroupsForUser({ Username: username, UserPoolId: userPoolId }).promise(); // Inject group info into token claims event.response = { claimsOverrideDetails: { claimsToAddOrOverride: { 'cognito:groups': groups.Groups.map(g => g.GroupName), 'custom:user_role': groups.Groups[0]?.GroupName || 'RegularUserGroup' } } }; } catch (err) { console.error('Failed to fetch user groups:', err); } return event; }; # Lambda Execution Role for Pre-Token Trigger LambdaExecutionRole: Type: AWS::IAM::Role Properties: RoleName: ${self:service}-lambda-exec-role-${self:provider.stage} AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: { Service: lambda.amazonaws.com } Action: sts:AssumeRole Policies: - PolicyName: CognitoAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: cognito-idp:AdminListGroupsForUser Resource: !GetAtt UserPool.Arn - PolicyName: LambdaLogging PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* # Attach Pre-Token Trigger to User Pool UserPoolPreTokenTrigger: Type: AWS::Cognito::UserPoolTrigger Properties: UserPoolId: !Ref UserPool TriggerType: PreTokenGeneration LambdaArn: !GetAtt PreTokenGeneratorLambda.Arn
Step 2: Lambda Handler Examples
Add these files in a handlers/ directory to implement your business logic, with optional extra permission checks.
handlers/getUser.js (Admin-only)
exports.handler = async (event) => { // Optional: Double-check user is in AdminGroup (extra security layer) const userGroups = event.requestContext.authorizer.claims['cognito:groups']; if (!userGroups.includes('AdminGroup')) { return { statusCode: 403, body: JSON.stringify({ message: 'Forbidden: Admin access required' }) }; } // Your admin-only logic here return { statusCode: 200, body: JSON.stringify({ user: 'Admin-specific user data' }) }; };
handlers/getVehicle.js (All users)
exports.handler = async (event) => { // Optional: Verify user is in an allowed group const userGroups = event.requestContext.authorizer.claims['cognito:groups']; if (!userGroups.includes('AdminGroup') && !userGroups.includes('RegularUserGroup')) { return { statusCode: 403, body: JSON.stringify({ message: 'Forbidden' }) }; } // Your vehicle fetch logic here return { statusCode: 200, body: JSON.stringify({ vehicles: ['Sedan', 'Truck'] }) }; };
handlers/postVehicle.js (Admin-only)
exports.handler = async (event) => { const userGroups = event.requestContext.authorizer.claims['cognito:groups']; if (!userGroups.includes('AdminGroup')) { return { statusCode: 403, body: JSON.stringify({ message: 'Forbidden: Admin access required' }) }; } // Your vehicle creation logic here return { statusCode: 201, body: JSON.stringify({ message: 'Vehicle created successfully' }) }; };
Step 3: Test the Setup
- Deploy the stack: Run
serverless deployin your project directory. - Create users: Use the AWS Console or CLI to create two Cognito users, then add one to
AdminGroupand the other toRegularUserGroup. - Authenticate to get a token:
aws cognito-idp initiate-auth --client-id <YOUR_USER_POOL_CLIENT_ID> --auth-flow USER_PASSWORD_AUTH --auth-parameters USERNAME=<USER_EMAIL>,PASSWORD=<USER_PASSWORD> - Call the APIs: Use tools like Postman or curl to send requests with the
Authorization: Bearer <ACCESS_TOKEN>header:- Admin users can access all three APIs.
- Regular users will get a 403 error for
GET /userandPOST /vehicle, but can accessGET /vehicle.
内容的提问来源于stack exchange,提问作者Philipp

