API 网关为 iOS (Objective-C) 生成的 SDK,授权用户使用 Cognito 用户池
API Gateway generated SDK for iOS (Objective-C) with Cognito User Pool for authorized users
我已经使用 AWS API 网关部署了一个 API,我正在尝试通过 iOS 设备访问它。一些端点支持未经授权的用户,我可以轻松访问它们,但其他端点不支持,我无法查询它们。我使用新的 Cognito 用户池功能进行身份验证,在使用 Web 应用程序(或邮递员)时工作正常。
首先,即使某些端点受到保护(如下图所示的控制台),当我部署 API 并为 iOS 生成 SDK 时(Objective-C), 我可以在 README 文件中阅读:"All endpoints do not require authorization."
然后,当我 运行 来自 AWS 文档的以下身份验证代码时,一切似乎都正常工作:
AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionEUWest1 credentialsProvider:nil];
AWSCognitoIdentityUserPoolConfiguration *userPoolConfiguration = [[AWSCognitoIdentityUserPoolConfiguration alloc] initWithClientId:ClientId clientSecret:nil poolId:UserPoolId];
[AWSCognitoIdentityUserPool registerCognitoIdentityUserPoolWithConfiguration:serviceConfiguration userPoolConfiguration:userPoolConfiguration forKey:@"UserPool"];
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:@"UserPool"];
AWSCognitoIdentityUser *user = [pool getUser];
[[user getSession:username password:password validationData:nil] continueWithBlock:^id(AWSTask<AWSCognitoIdentityUserSession *> *task) {
if (task.error) {
NSLog(@"Could not get user session. Error: %@", task.error);
} else if (task.exception) {
NSLog(@"Could not get user session. Exception: %@", task.exception);
} else {
NSLog(@"Successfully retrieved user session data");
AWSCognitoIdentityUserSession *session = (AWSCognitoIdentityUserSession *) task.result;
NSMutableString *poolId = [[NSMutableString alloc] initWithString:@"cognito-idp.eu-west-1.amazonaws.com/"];
[poolId appendString:UserPoolId];
NSString *tokenStr = [session.idToken tokenString];
NSDictionary *tokens = [[NSDictionary alloc] initWithObjectsAndKeys:tokenStr, poolId, nil];
CognitoPoolIdentityProvider *idProvider = [[CognitoPoolIdentityProvider alloc] init];
[idProvider addTokens:tokens];
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionEUWest1 identityPoolId:IdentityPoolId identityProviderManager:idProvider];
[credentialsProvider clearKeychain];
[credentialsProvider clearCredentials];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionEUWest1
credentialsProvider:credentialsProvider];
[[credentialsProvider getIdentityId] continueWithBlock:^id _Nullable(AWSTask<NSString *> * _Nonnull task) {
if (task.error) {
NSLog(@"Could not get identity id: %@", task.error);
} else if (task.exception) {
NSLog(@"Could not get identity id: %@", task.exception);
} else {
NSLog(@"Identity id: %@", task.result);
}
return nil;
}];
}
return nil;
}];
我按照 中的说明实施了 CognitoPoolIdentityProvider。
@interface CognitoPoolIdentityProvider ()
@property (strong, nonatomic) NSDictionary *tokens;
@end
@implementation CognitoPoolIdentityProvider
- (AWSTask<NSDictionary *> *)logins {
return [AWSTask taskWithResult:self.tokens];
}
- (void)addTokens:(NSDictionary *)tokens {
self.tokens = tokens;
}
@end
我设法获得了正确的令牌(使用邮递员测试)和用户 ID:
2016-12-27 12:43:35.760 AskHub[26625:10037234] AWSiOSSDK v2.4.11 [Debug] AWSURLResponseSerialization.m line:63 | -[AWSJSONResponseSerializer responseObjectForResponse:originalRequest:currentRequest:data:error:] | Response body:
{"IdentityId":"eu-west-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"}
2016-12-27 12:43:35.766 AskHub[26625:10037234] Identity id: eu-west-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
但是,当我运行以下代码访问受保护端点时,CloudFront 认为我未通过身份验证。
APIHealthClient *apiClient = [APIHealthClient defaultClient];
APIAskHubRequest *initReq = [[APIAskHubRequest alloc] init];
initReq.message = @"FIN";
NSLog(@"Sending initial request");
[[apiClient webhooksAskhubPost:initReq] continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(@"Could not send initial request: %@", task.error);
} else if (task.exception) {
NSLog(@"Could not send initial request: %@", task.exception);
} else {
NSLog(@"Successfully sent initial request");
}
return nil;
}];
回复:
2016-12-27 12:56:25.247 AskHub[26784:10046562] Could not send initial request: Error Domain=com.amazonaws.AWSAPIGatewayErrorDomain Code=1 "(null)" UserInfo={HTTPBody={
message = Unauthorized;
}, HTTPHeaderFields={type = immutable dict, count = 9,
entries =>
3 : Via = {contents = "X.X XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.cloudfront.net (CloudFront)"}
4 : x-amzn-ErrorType = {contents = "UnauthorizedException"}
5 : Content-Type = {contents = "application/json"}
6 : Content-Length = {contents = "27"}
7 : Connection = {contents = "keep-alive"}
8 : x-amzn-RequestId = {contents = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"}
9 : Date = {contents = "Tue, 27 Dec 2016 11:56:25 GMT"}
10 : X-Cache = {contents = "Error from cloudfront"}
11 : X-Amz-Cf-Id = {contents = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}
}
}
我是不是漏掉了什么?自动生成的SDK是否支持User Pool认证?
根据我看到的代码,您正在清除凭据但从未获得凭据。快速回顾一下,您似乎正在正确设置服务配置和身份验证,但您从未调用凭据。
如果是这种情况,即使您的应用程序会进行身份验证,凭据提供程序也永远不会获取登录字典。
(您可以在 AWS 控制台中验证这一点,如果您查看您尝试用于访问资源的 identityId,您可以查看 "logins" 计数。如果它是 0,则您是未登录。)
为了进行身份验证,您需要让凭据提供程序调用您的 identityProviderManager "logins" 方法。
当您执行 "getCredentialsForIdentity" 时会发生该调用,这在 SDK 中是 "credentials"。
通常顺序是 GetID,然后是 GetCredentialsForId。在 IOS SDK 中,GetId 发生在 "getSession"(所以你应该有),而 GetCredentials 发生在 "credentials"
(请注意,每个 SDK 都有不同的命名(例如,Mobile Hub 与 IOS SDK)但重要的是您的凭据提供程序会获得一个登录字典。使用详细的日志记录,您实际上应该查看记录的 "logins" 词典,这将向您证明正在向凭据提供程序提供登录词典。
我发现两种身份验证模式之间存在混淆:使用凭据提供程序或仅将 JWT 令牌添加到 "Authorization" header 参数(或您的自定义参数名称)如果您在创建授权方时指定了一个)。
第一种方法对我不起作用,尽管它是我在使用 iOS SDK 时在文档中找到的唯一方法。第二种方法工作得很好,但为了能够在不侵入 SDK 的情况下使用它,请不要忘记在 [=17] 中指定 API 时添加 "Authorization" header =] 网关。
我已经使用 AWS API 网关部署了一个 API,我正在尝试通过 iOS 设备访问它。一些端点支持未经授权的用户,我可以轻松访问它们,但其他端点不支持,我无法查询它们。我使用新的 Cognito 用户池功能进行身份验证,在使用 Web 应用程序(或邮递员)时工作正常。
首先,即使某些端点受到保护(如下图所示的控制台),当我部署 API 并为 iOS 生成 SDK 时(Objective-C), 我可以在 README 文件中阅读:"All endpoints do not require authorization."
然后,当我 运行 来自 AWS 文档的以下身份验证代码时,一切似乎都正常工作:
AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionEUWest1 credentialsProvider:nil];
AWSCognitoIdentityUserPoolConfiguration *userPoolConfiguration = [[AWSCognitoIdentityUserPoolConfiguration alloc] initWithClientId:ClientId clientSecret:nil poolId:UserPoolId];
[AWSCognitoIdentityUserPool registerCognitoIdentityUserPoolWithConfiguration:serviceConfiguration userPoolConfiguration:userPoolConfiguration forKey:@"UserPool"];
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:@"UserPool"];
AWSCognitoIdentityUser *user = [pool getUser];
[[user getSession:username password:password validationData:nil] continueWithBlock:^id(AWSTask<AWSCognitoIdentityUserSession *> *task) {
if (task.error) {
NSLog(@"Could not get user session. Error: %@", task.error);
} else if (task.exception) {
NSLog(@"Could not get user session. Exception: %@", task.exception);
} else {
NSLog(@"Successfully retrieved user session data");
AWSCognitoIdentityUserSession *session = (AWSCognitoIdentityUserSession *) task.result;
NSMutableString *poolId = [[NSMutableString alloc] initWithString:@"cognito-idp.eu-west-1.amazonaws.com/"];
[poolId appendString:UserPoolId];
NSString *tokenStr = [session.idToken tokenString];
NSDictionary *tokens = [[NSDictionary alloc] initWithObjectsAndKeys:tokenStr, poolId, nil];
CognitoPoolIdentityProvider *idProvider = [[CognitoPoolIdentityProvider alloc] init];
[idProvider addTokens:tokens];
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionEUWest1 identityPoolId:IdentityPoolId identityProviderManager:idProvider];
[credentialsProvider clearKeychain];
[credentialsProvider clearCredentials];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionEUWest1
credentialsProvider:credentialsProvider];
[[credentialsProvider getIdentityId] continueWithBlock:^id _Nullable(AWSTask<NSString *> * _Nonnull task) {
if (task.error) {
NSLog(@"Could not get identity id: %@", task.error);
} else if (task.exception) {
NSLog(@"Could not get identity id: %@", task.exception);
} else {
NSLog(@"Identity id: %@", task.result);
}
return nil;
}];
}
return nil;
}];
我按照
@interface CognitoPoolIdentityProvider ()
@property (strong, nonatomic) NSDictionary *tokens;
@end
@implementation CognitoPoolIdentityProvider
- (AWSTask<NSDictionary *> *)logins {
return [AWSTask taskWithResult:self.tokens];
}
- (void)addTokens:(NSDictionary *)tokens {
self.tokens = tokens;
}
@end
我设法获得了正确的令牌(使用邮递员测试)和用户 ID:
2016-12-27 12:43:35.760 AskHub[26625:10037234] AWSiOSSDK v2.4.11 [Debug] AWSURLResponseSerialization.m line:63 | -[AWSJSONResponseSerializer responseObjectForResponse:originalRequest:currentRequest:data:error:] | Response body: {"IdentityId":"eu-west-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"} 2016-12-27 12:43:35.766 AskHub[26625:10037234] Identity id: eu-west-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
但是,当我运行以下代码访问受保护端点时,CloudFront 认为我未通过身份验证。
APIHealthClient *apiClient = [APIHealthClient defaultClient];
APIAskHubRequest *initReq = [[APIAskHubRequest alloc] init];
initReq.message = @"FIN";
NSLog(@"Sending initial request");
[[apiClient webhooksAskhubPost:initReq] continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(@"Could not send initial request: %@", task.error);
} else if (task.exception) {
NSLog(@"Could not send initial request: %@", task.exception);
} else {
NSLog(@"Successfully sent initial request");
}
return nil;
}];
回复:
2016-12-27 12:56:25.247 AskHub[26784:10046562] Could not send initial request: Error Domain=com.amazonaws.AWSAPIGatewayErrorDomain Code=1 "(null)" UserInfo={HTTPBody={ message = Unauthorized; }, HTTPHeaderFields={type = immutable dict, count = 9, entries => 3 : Via = {contents = "X.X XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.cloudfront.net (CloudFront)"} 4 : x-amzn-ErrorType = {contents = "UnauthorizedException"} 5 : Content-Type = {contents = "application/json"} 6 : Content-Length = {contents = "27"} 7 : Connection = {contents = "keep-alive"} 8 : x-amzn-RequestId = {contents = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"} 9 : Date = {contents = "Tue, 27 Dec 2016 11:56:25 GMT"} 10 : X-Cache = {contents = "Error from cloudfront"} 11 : X-Amz-Cf-Id = {contents = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"} } }
我是不是漏掉了什么?自动生成的SDK是否支持User Pool认证?
根据我看到的代码,您正在清除凭据但从未获得凭据。快速回顾一下,您似乎正在正确设置服务配置和身份验证,但您从未调用凭据。
如果是这种情况,即使您的应用程序会进行身份验证,凭据提供程序也永远不会获取登录字典。
(您可以在 AWS 控制台中验证这一点,如果您查看您尝试用于访问资源的 identityId,您可以查看 "logins" 计数。如果它是 0,则您是未登录。)
为了进行身份验证,您需要让凭据提供程序调用您的 identityProviderManager "logins" 方法。
当您执行 "getCredentialsForIdentity" 时会发生该调用,这在 SDK 中是 "credentials"。
通常顺序是 GetID,然后是 GetCredentialsForId。在 IOS SDK 中,GetId 发生在 "getSession"(所以你应该有),而 GetCredentials 发生在 "credentials"
(请注意,每个 SDK 都有不同的命名(例如,Mobile Hub 与 IOS SDK)但重要的是您的凭据提供程序会获得一个登录字典。使用详细的日志记录,您实际上应该查看记录的 "logins" 词典,这将向您证明正在向凭据提供程序提供登录词典。
我发现两种身份验证模式之间存在混淆:使用凭据提供程序或仅将 JWT 令牌添加到 "Authorization" header 参数(或您的自定义参数名称)如果您在创建授权方时指定了一个)。
第一种方法对我不起作用,尽管它是我在使用 iOS SDK 时在文档中找到的唯一方法。第二种方法工作得很好,但为了能够在不侵入 SDK 的情况下使用它,请不要忘记在 [=17] 中指定 API 时添加 "Authorization" header =] 网关。