AWS Cognito与Lambda:为用户池添加LinkedIn联合身份
Nice work getting your Cognito user pool, identity pool, and LinkedIn app set up already! Since Cognito doesn't have native support for LinkedIn, using a Lambda function to bridge the gap is exactly the right approach. Here's a detailed, step-by-step guide to make this integration work smoothly:
1. 提前准备工作(确认已完成)
Before diving in, double-check these boxes to avoid roadblocks:
- Your LinkedIn app has the correct redirect URIs configured and the
r_liteprofile+r_emailaddressscopes enabled - Your Cognito user pool has a custom attribute
custom:linkedin_user_idcreated (to store unique LinkedIn user IDs) - Your Cognito identity pool is linked to your user pool as an identity provider
2. 创建核心Lambda函数(Python示例)
This function will handle exchanging LinkedIn auth codes for user data, syncing users to your Cognito pool, and returning valid Cognito tokens to your frontend.
First, store sensitive values (LinkedIn client ID/secret, Cognito IDs) in AWS Secrets Manager instead of hardcoding them. Here's the core logic:
import boto3 import requests import json # Initialize clients cognito_client = boto3.client('cognito-idp') secrets_manager = boto3.client('secretsmanager') def get_secrets(): """Fetch sensitive values from Secrets Manager""" secret_resp = secrets_manager.get_secret_value(SecretId='linkedin-cognito-integration') return json.loads(secret_resp['SecretString']) def lambda_handler(event, context): secrets = get_secrets() linkedin_code = event['code'] # Step 1: Exchange LinkedIn auth code for access token token_resp = requests.post( 'https://www.linkedin.com/oauth/v2/accessToken', data={ 'grant_type': 'authorization_code', 'code': linkedin_code, 'client_id': secrets['linkedin_client_id'], 'client_secret': secrets['linkedin_client_secret'], 'redirect_uri': secrets['linkedin_redirect_uri'] } ) token_data = token_resp.json() access_token = token_data['access_token'] # Step 2: Fetch LinkedIn user profile and email profile_resp = requests.get( 'https://api.linkedin.com/v2/me', headers={'Authorization': f'Bearer {access_token}'} ) profile_data = profile_resp.json() linkedin_user_id = profile_data['id'] first_name = profile_data['localizedFirstName'] last_name = profile_data['localizedLastName'] email_resp = requests.get( 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', headers={'Authorization': f'Bearer {access_token}'} ) email_data = email_resp.json() user_email = email_data['elements'][0]['handle~']['emailAddress'] # Step 3: Sync user to Cognito user pool try: # Check if user already exists cognito_client.admin_get_user( UserPoolId=secrets['cognito_user_pool_id'], Username=user_email ) # Update existing user attributes cognito_client.admin_update_user_attributes( UserPoolId=secrets['cognito_user_pool_id'], Username=user_email, UserAttributes=[ {'Name': 'given_name', 'Value': first_name}, {'Name': 'family_name', 'Value': last_name}, {'Name': 'custom:linkedin_user_id', 'Value': linkedin_user_id} ] ) except cognito_client.exceptions.UserNotFoundException: # Create new user in Cognito cognito_client.admin_create_user( UserPoolId=secrets['cognito_user_pool_id'], Username=user_email, UserAttributes=[ {'Name': 'email', 'Value': user_email}, {'Name': 'email_verified', 'Value': 'true'}, {'Name': 'given_name', 'Value': first_name}, {'Name': 'family_name', 'Value': last_name}, {'Name': 'custom:linkedin_user_id', 'Value': linkedin_user_id} ], MessageAction='SUPPRESS' # Skip welcome email ) # Set temporary password (we'll auto-authenticate next) cognito_client.admin_set_user_password( UserPoolId=secrets['cognito_user_pool_id'], Username=user_email, Password='TempAuth123!', Permanent=False ) # Step 4: Return Cognito auth tokens to frontend auth_resp = cognito_client.admin_initiate_auth( UserPoolId=secrets['cognito_user_pool_id'], ClientId=secrets['cognito_app_client_id'], AuthFlow='ADMIN_USER_PASSWORD_AUTH', AuthParameters={ 'USERNAME': user_email, 'PASSWORD': 'TempAuth123!' } ) return { 'statusCode': 200, 'body': json.dumps({ 'tokens': auth_resp['AuthenticationResult'] }) }
3. Configure Lambda Permissions
Attach a policy to your Lambda's execution role that allows these actions:
cognito-idp:AdminCreateUsercognito-idp:AdminUpdateUserAttributescognito-idp:AdminGetUsercognito-idp:AdminInitiateAuthcognito-idp:AdminSetUserPasswordsecretsmanager:GetSecretValue(for fetching your stored secrets)
If your Lambda runs in a VPC, ensure it has access to the internet (via a NAT Gateway) to call LinkedIn's APIs.
4. Frontend Integration Flow
- Redirect users to LinkedIn's authorization URL:
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=r_liteprofile%20r_emailaddress - After user authorization, LinkedIn will redirect back to your frontend with a
codeparameter. - Send this
codeto your Lambda function (expose it via API Gateway for HTTP access). - Use the returned Cognito tokens to authenticate users in your app and interact with your Cognito identity pool for AWS resource access.
5. Optional: Skip Temporary Password Step
For a smoother flow, use Cognito's custom authentication flow instead of ADMIN_USER_PASSWORD_AUTH. Update your Lambda to handle DefineAuthChallenge, CreateAuthChallenge, and VerifyAuthChallengeResponse triggers—this lets you directly authenticate users without a temporary password.
内容的提问来源于stack exchange,提问作者Jez D

