如何使用 AWS CDK v2 通过 Cognito 创建同时使用客户端凭证和用户身份验证流的 API 网关?

问题描述 投票:0回答:1

我的 API 需要两种类型的 OAuth 流程,因为我有通过网络注册的用户以及直接与 API 对话的应用程序,例如Grafana、用户集成等

我已在基础设施中设置了两个流程,但只有用户登录有效。客户端凭据不会针对 APIGW 端点进行授权。

这是我的 Cognito

AuthStack

export class AuthStack extends Stack {
    public userPool!: UserPool;
    private userPoolClient!: UserPoolClient;

    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);

        this.userPool = new UserPool(this, "MyUserPool", {
            selfSignUpEnabled: true,
            signInAliases: {
                username: true,
                email: true
            }
        });

        this.userPool.addDomain("MyUserPoolDomain", {
            cognitoDomain: {
                domainPrefix: "my-auth-prototype"
            }
        });

        new CfnUserPoolGroup(this, "MyAdminPoolGroup", {
            userPoolId: this.userPool.userPoolId,
            groupName: "admin"
        });

        const clientReadServerScope = new ResourceServerScope({
            scopeName: "client.read",
            scopeDescription: "client read scope",
        });

        const resourceServer = new UserPoolResourceServer(this, "ClientCredentialsResourceServer", {
            identifier: "client-credentials-resource-server",
            userPool: this.userPool,
            scopes: [clientReadServerScope],
        });

        this.userPoolClient = this.userPool.addClient("MyUserPoolClient", {
            accessTokenValidity: Duration.minutes(60),
            generateSecret: true,
            refreshTokenValidity: Duration.days(1),
            enableTokenRevocation: true,
            oAuth: {
                flows: {
                    clientCredentials: true,
                },
                scopes: [OAuthScope.resourceServer(resourceServer, clientReadServerScope)],
            },
            authFlows: {
                adminUserPassword: true,
                custom: true,
                userPassword: true,
                userSrp: true
            }
        });
    }    
}

ApiStack

export class ApiStack extends Stack {
    constructor(scope: Construct, id: string, props: ApiStackProps) {
        super(scope, id, props);

        const api = new RestApi(this, "MyApi", {
            binaryMediaTypes: ["*/*"]
        });

        //Cognito authorizor
        const authorizer = new CognitoUserPoolsAuthorizer(this, "MyApiAuthorizor", {
            cognitoUserPools: [props.userPool],
            identitySource: "method.request.header.Authorization"
        });
        authorizer._attachToApi(api);

        //Cognito options
        const optionsWithAuth: MethodOptions = {
            authorizationType: AuthorizationType.COGNITO,
            authorizer: {
                authorizerId: authorizer.authorizerId
            }
        };

        //CORS options
        const optionsWithCors: ResourceOptions = {
            defaultCorsPreflightOptions: {
                allowOrigins: Cors.ALL_ORIGINS,
                allowMethods: Cors.ALL_METHODS
            }
        };

        //base resource
        const dataPipelineResource = api.root.addResource("my-resource", optionsWithCors);

        dataPipelineResource.addMethod("GET", props.lambdaIntegration, optionsWithAuth);
    }
}

我能够像这样检索有效的

access_token

export const getClientToken = async (clientId: string, clientSecret: string): Promise<string | null> => {
    const authEndpoint = "https://my-auth-prototype.auth.us-east-1.amazoncognito.com/oauth2/token";
    const res = await fetch(authEndpoint, {
        method: "POST",
        body: new URLSearchParams({
            "grant_type": "client_credentials",
        }),
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64")
        }
    });
    const data = await res.json();
    return data.access_token;
};

但是,如果我将它用于上面定义的 API 网关中的

my-resource
GET 端点,我会收到
401 Unauthorized
响应。

如何连接两者?这和我定义的

ResourceServerScope
有关系吗?我错过了什么?

amazon-web-services oauth-2.0 aws-api-gateway amazon-cognito aws-cdk
1个回答
0
投票

我设法通过拆开这个答案来解决这个问题。我像这样更改了 AuthStack 的相关部分:

const myServerScope = new ResourceServerScope({
    scopeName: "my-resource",
    scopeDescription: "my-resource scope",
});

const resourceServer = new UserPoolResourceServer(this, "ClientCredentialsResourceServer", {
    identifier: "prod",
    userPool: this.userPool,
    scopes: [myServerScope],
});

this.userPoolClient = this.userPool.addClient("MyUserPoolClient", {
    generateSecret: true,
    enableTokenRevocation: true,
    accessTokenValidity: Duration.minutes(60),
    refreshTokenValidity: Duration.days(1),
    oAuth: {
        flows: {
            clientCredentials: true
        },
        scopes: [
            OAuthScope.resourceServer(resourceServer, myServerScope)
        ],
    },
    authFlows: {
        adminUserPassword: true,
        userPassword: true,
        userSrp: true,
        custom: true
    }
});

然后,将其与方法选项中的

authorizationScopes
属性相匹配,在
ApiStack
:

const optionsWithAuth: MethodOptions = {
    authorizationType: AuthorizationType.COGNITO,
    authorizer: {
        authorizerId: authorizer.authorizerId
    },
    authorizationScopes: [
        "prod/my-resource"
    ]
};

用户和客户端凭据都可以使用 AccessKey 对 API 进行身份验证,并且一切都运行良好。

© www.soinside.com 2019 - 2024. All rights reserved.