multi-tenant Phase 2: per-user Google OAuth
- workspace-mcp: add proxy.py (port 8080) that reads X-Actor-Id header,
fetches per-user Google credentials from Secrets Manager, writes creds
file, sets USER_GOOGLE_EMAIL, proxies to workspace-mcp on port 8081
- workspace-mcp: update bootstrap to start workspace-mcp on 8081 + proxy on 8080
- workspace-mcp: update Dockerfile to include proxy.py
- oauth-handler Lambda: new Lambda with /oauth/start + /oauth/callback
routes; exchanges Google auth code, stores tokens in Secrets Manager
at agent-claw/google-credentials/{actor_id_safe}, updates DynamoDB
- CDK: add OAuthHandler Lambda + GET /oauth/start + /oauth/callback routes
- CDK: remove shared google-workspace-credentials secret; add per-user
secret IAM grants (agent-claw/google-credentials/*) for workspace-mcp
role, runtime1 role, and oauth-handler role
- CDK: output OAuthStartUrl + OAuthRedirectUri
- agent-runner: pass google_email in user_profile payload
- main.py: pass actor_id as X-Actor-Id header in workspace-mcp MCP calls;
skip workspace-mcp if user has no google_email; add connect_google_account
tool that generates OAuth URL for the current user
- main.py: include google_email in user_context for system prompt
- agentcore.json: add OAUTH_START_URL env var for agent runtime
This commit is contained in:
@@ -387,7 +387,7 @@
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||
"S3Key": "7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387.zip"
|
||||
"S3Key": "6f6fdf79f33a947f3e50ffd783a72d04ab5f29ba299a5d51b3ecd2c2eb311370.zip"
|
||||
},
|
||||
"Environment": {
|
||||
"Variables": {
|
||||
@@ -423,7 +423,7 @@
|
||||
],
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/AgentRunner/Resource",
|
||||
"aws:asset:path": "asset.7053cd1618f5f520a7aac409588128f920d8fe76791c1dbcc65610454d1a5387",
|
||||
"aws:asset:path": "asset.6f6fdf79f33a947f3e50ffd783a72d04ab5f29ba299a5d51b3ecd2c2eb311370",
|
||||
"aws:asset:is-bundled": false,
|
||||
"aws:asset:property": "Code"
|
||||
}
|
||||
@@ -545,6 +545,156 @@
|
||||
"aws:cdk:path": "AgentClawStack/WebhookApi/POST--telegram/Resource"
|
||||
}
|
||||
},
|
||||
"WebhookApiGEToauthstartOAuthStartIntegrationA546443F": {
|
||||
"Type": "AWS::ApiGatewayV2::Integration",
|
||||
"Properties": {
|
||||
"ApiId": {
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
"IntegrationType": "AWS_PROXY",
|
||||
"IntegrationUri": {
|
||||
"Fn::GetAtt": [
|
||||
"OAuthHandlerC97C2476",
|
||||
"Arn"
|
||||
]
|
||||
},
|
||||
"PayloadFormatVersion": "2.0"
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/WebhookApi/GET--oauth--start/OAuthStartIntegration/Resource"
|
||||
}
|
||||
},
|
||||
"WebhookApiGEToauthstartOAuthStartIntegrationPermission38BAEF6D": {
|
||||
"Type": "AWS::Lambda::Permission",
|
||||
"Properties": {
|
||||
"Action": "lambda:InvokeFunction",
|
||||
"FunctionName": {
|
||||
"Fn::GetAtt": [
|
||||
"OAuthHandlerC97C2476",
|
||||
"Arn"
|
||||
]
|
||||
},
|
||||
"Principal": "apigateway.amazonaws.com",
|
||||
"SourceArn": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"arn:",
|
||||
{
|
||||
"Ref": "AWS::Partition"
|
||||
},
|
||||
":execute-api:us-east-1:495395224548:",
|
||||
{
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
"/*/*/oauth/start"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/WebhookApi/GET--oauth--start/OAuthStartIntegration-Permission"
|
||||
}
|
||||
},
|
||||
"WebhookApiGEToauthstart6DCA713A": {
|
||||
"Type": "AWS::ApiGatewayV2::Route",
|
||||
"Properties": {
|
||||
"ApiId": {
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
"AuthorizationType": "NONE",
|
||||
"RouteKey": "GET /oauth/start",
|
||||
"Target": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"integrations/",
|
||||
{
|
||||
"Ref": "WebhookApiGEToauthstartOAuthStartIntegrationA546443F"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/WebhookApi/GET--oauth--start/Resource"
|
||||
}
|
||||
},
|
||||
"WebhookApiGEToauthcallbackOAuthCallbackIntegrationCFBBEB09": {
|
||||
"Type": "AWS::ApiGatewayV2::Integration",
|
||||
"Properties": {
|
||||
"ApiId": {
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
"IntegrationType": "AWS_PROXY",
|
||||
"IntegrationUri": {
|
||||
"Fn::GetAtt": [
|
||||
"OAuthHandlerC97C2476",
|
||||
"Arn"
|
||||
]
|
||||
},
|
||||
"PayloadFormatVersion": "2.0"
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/WebhookApi/GET--oauth--callback/OAuthCallbackIntegration/Resource"
|
||||
}
|
||||
},
|
||||
"WebhookApiGEToauthcallbackOAuthCallbackIntegrationPermission6BA3A5AD": {
|
||||
"Type": "AWS::Lambda::Permission",
|
||||
"Properties": {
|
||||
"Action": "lambda:InvokeFunction",
|
||||
"FunctionName": {
|
||||
"Fn::GetAtt": [
|
||||
"OAuthHandlerC97C2476",
|
||||
"Arn"
|
||||
]
|
||||
},
|
||||
"Principal": "apigateway.amazonaws.com",
|
||||
"SourceArn": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"arn:",
|
||||
{
|
||||
"Ref": "AWS::Partition"
|
||||
},
|
||||
":execute-api:us-east-1:495395224548:",
|
||||
{
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
"/*/*/oauth/callback"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/WebhookApi/GET--oauth--callback/OAuthCallbackIntegration-Permission"
|
||||
}
|
||||
},
|
||||
"WebhookApiGEToauthcallbackFC1F6BCD": {
|
||||
"Type": "AWS::ApiGatewayV2::Route",
|
||||
"Properties": {
|
||||
"ApiId": {
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
"AuthorizationType": "NONE",
|
||||
"RouteKey": "GET /oauth/callback",
|
||||
"Target": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"integrations/",
|
||||
{
|
||||
"Ref": "WebhookApiGEToauthcallbackOAuthCallbackIntegrationCFBBEB09"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/WebhookApi/GET--oauth--callback/Resource"
|
||||
}
|
||||
},
|
||||
"Runtime1RoleA7A82078": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
@@ -674,29 +824,16 @@
|
||||
{
|
||||
"Ref": "AWS::Partition"
|
||||
},
|
||||
":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-workspace-credentials-??????"
|
||||
":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-??????"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Action": [
|
||||
"secretsmanager:GetSecretValue",
|
||||
"secretsmanager:DescribeSecret"
|
||||
],
|
||||
"Action": "secretsmanager:GetSecretValue",
|
||||
"Effect": "Allow",
|
||||
"Resource": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"arn:",
|
||||
{
|
||||
"Ref": "AWS::Partition"
|
||||
},
|
||||
":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-??????"
|
||||
]
|
||||
]
|
||||
}
|
||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*",
|
||||
"Sid": "PerUserGoogleCredentialsReadRuntime"
|
||||
}
|
||||
],
|
||||
"Version": "2012-10-17"
|
||||
@@ -731,11 +868,68 @@
|
||||
{
|
||||
"Ref": "AWS::Partition"
|
||||
},
|
||||
":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-workspace-credentials-??????"
|
||||
":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-??????"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Action": "secretsmanager:GetSecretValue",
|
||||
"Effect": "Allow",
|
||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*",
|
||||
"Sid": "PerUserGoogleCredentialsRead"
|
||||
}
|
||||
],
|
||||
"Version": "2012-10-17"
|
||||
},
|
||||
"PolicyName": "WorkspaceMcpRolePolicy5B8B0072",
|
||||
"Roles": [
|
||||
"agent-claw-workspace-mcp-role"
|
||||
]
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/WorkspaceMcpRole/Policy/Resource"
|
||||
}
|
||||
},
|
||||
"OAuthHandlerServiceRole9CDCCF9E": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "lambda.amazonaws.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": "2012-10-17"
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"arn:",
|
||||
{
|
||||
"Ref": "AWS::Partition"
|
||||
},
|
||||
":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/OAuthHandler/ServiceRole/Resource"
|
||||
}
|
||||
},
|
||||
"OAuthHandlerServiceRoleDefaultPolicy69D90416": {
|
||||
"Type": "AWS::IAM::Policy",
|
||||
"Properties": {
|
||||
"PolicyDocument": {
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"secretsmanager:GetSecretValue",
|
||||
@@ -754,17 +948,132 @@
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Action": [
|
||||
"dynamodb:BatchGetItem",
|
||||
"dynamodb:Query",
|
||||
"dynamodb:GetItem",
|
||||
"dynamodb:Scan",
|
||||
"dynamodb:ConditionCheckItem",
|
||||
"dynamodb:BatchWriteItem",
|
||||
"dynamodb:PutItem",
|
||||
"dynamodb:UpdateItem",
|
||||
"dynamodb:DeleteItem",
|
||||
"dynamodb:DescribeTable"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": [
|
||||
{
|
||||
"Fn::GetAtt": [
|
||||
"UsersTable9725E9C8",
|
||||
"Arn"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Action": [
|
||||
"dynamodb:GetRecords",
|
||||
"dynamodb:GetShardIterator"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": [
|
||||
{
|
||||
"Fn::GetAtt": [
|
||||
"UsersTable9725E9C8",
|
||||
"Arn"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Action": [
|
||||
"secretsmanager:CreateSecret",
|
||||
"secretsmanager:PutSecretValue",
|
||||
"secretsmanager:GetSecretValue"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*",
|
||||
"Sid": "PerUserGoogleCredentialsWrite"
|
||||
}
|
||||
],
|
||||
"Version": "2012-10-17"
|
||||
},
|
||||
"PolicyName": "WorkspaceMcpRolePolicy5B8B0072",
|
||||
"PolicyName": "OAuthHandlerServiceRoleDefaultPolicy69D90416",
|
||||
"Roles": [
|
||||
"agent-claw-workspace-mcp-role"
|
||||
{
|
||||
"Ref": "OAuthHandlerServiceRole9CDCCF9E"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/WorkspaceMcpRole/Policy/Resource"
|
||||
"aws:cdk:path": "AgentClawStack/OAuthHandler/ServiceRole/DefaultPolicy/Resource"
|
||||
}
|
||||
},
|
||||
"OAuthHandlerC97C2476": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||
"S3Key": "5be87975e51a6859dfad098b3d998a0bcd09a4f9a437bbf38923338fb559eb9e.zip"
|
||||
},
|
||||
"Environment": {
|
||||
"Variables": {
|
||||
"GOOGLE_OAUTH_CLIENT_SECRET_ARN": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"arn:",
|
||||
{
|
||||
"Ref": "AWS::Partition"
|
||||
},
|
||||
":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client"
|
||||
]
|
||||
]
|
||||
},
|
||||
"USERS_TABLE_NAME": {
|
||||
"Ref": "UsersTable9725E9C8"
|
||||
},
|
||||
"OAUTH_REDIRECT_URI": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"https://",
|
||||
{
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
".execute-api.us-east-1.",
|
||||
{
|
||||
"Ref": "AWS::URLSuffix"
|
||||
},
|
||||
"/oauth/callback"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"FunctionName": "agent-claw-oauth-handler",
|
||||
"Handler": "handler.handler",
|
||||
"MemorySize": 128,
|
||||
"Role": {
|
||||
"Fn::GetAtt": [
|
||||
"OAuthHandlerServiceRole9CDCCF9E",
|
||||
"Arn"
|
||||
]
|
||||
},
|
||||
"Runtime": "python3.12",
|
||||
"Timeout": 30
|
||||
},
|
||||
"DependsOn": [
|
||||
"OAuthHandlerServiceRoleDefaultPolicy69D90416",
|
||||
"OAuthHandlerServiceRole9CDCCF9E"
|
||||
],
|
||||
"Metadata": {
|
||||
"aws:cdk:path": "AgentClawStack/OAuthHandler/Resource",
|
||||
"aws:asset:path": "asset.5be87975e51a6859dfad098b3d998a0bcd09a4f9a437bbf38923338fb559eb9e",
|
||||
"aws:asset:is-bundled": false,
|
||||
"aws:asset:property": "Code"
|
||||
}
|
||||
},
|
||||
"CDKMetadata": {
|
||||
@@ -782,17 +1091,40 @@
|
||||
"Description": "workspace-mcp Lambda Function URL (MCP endpoint for Gmail/Calendar)",
|
||||
"Value": "https://25hugrzw4uwtueeg77jsmft6lq0wunmd.lambda-url.us-east-1.on.aws"
|
||||
},
|
||||
"GoogleCredentialsSecretArn": {
|
||||
"Description": "Google OAuth user credentials secret ARN",
|
||||
"OAuthStartUrl": {
|
||||
"Description": "Google OAuth start URL — set as OAUTH_START_URL in agentcore.json",
|
||||
"Value": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"arn:",
|
||||
"https://",
|
||||
{
|
||||
"Ref": "AWS::Partition"
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
":secretsmanager:us-east-1:495395224548:secret:agent-claw/google-workspace-credentials"
|
||||
".execute-api.us-east-1.",
|
||||
{
|
||||
"Ref": "AWS::URLSuffix"
|
||||
},
|
||||
"/oauth/start"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"OAuthRedirectUri": {
|
||||
"Description": "Google OAuth redirect URI — register in Google Cloud Console",
|
||||
"Value": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"https://",
|
||||
{
|
||||
"Ref": "WebhookApi28122C53"
|
||||
},
|
||||
".execute-api.us-east-1.",
|
||||
{
|
||||
"Ref": "AWS::URLSuffix"
|
||||
},
|
||||
"/oauth/callback"
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user