我的 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
有关系吗?我错过了什么?
我设法通过拆开这个答案来解决这个问题。我像这样更改了 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 进行身份验证,并且一切都运行良好。