从 unauth 切换到经过开发人员身份验证的 cognito 用户 - AWS iOS SDK
Switch from unauth to developer authenticated cognito user - AWS iOS SDK
总体问题:
我在前端使用开发人员身份验证时遇到问题 (iOS)。我知道我的后端会生成正确的令牌和 identityID,但我的刷新方法从未被调用过。我也看过样本,但我对发生的一切感到有点困惑。
流程说明:
目前我有一个带有登录按钮的登录屏幕。用户按下登录按钮,然后我的 api class 获取凭据,加密密码并将其存储在钥匙串中(现在已注释掉,因为它在模拟器上不起作用)。我的 DeveloperAuthenticatedIdentityProvider 称为我的应用程序 BusytimeAuthenticated。我已经完成了所有方法(我正在使用 AWS lambda 和 DynamoDB 对用户进行身份验证)我从未经身份验证的访问开始,它只允许我访问两种方法,即登录和注册。然后我想假设我的身份验证用户允许我调用我的其他方法。
我的API代码:
[AWSLogger defaultLogger].logLevel = AWSLogLevelVerbose;
id<AWSCognitoIdentityProvider> identityProvider = [[BusytimeAuthenticated alloc] initWithRegionType:AWSRegionUSEast1
identityId:nil
identityPoolId:@"SOMEIDENTITYPOOLID"
logins:@{@"SOMEPROVIDERNAME": @"SOMEUSERNAME"}
providerName:@"SOMEPROVIDERNAME"
];
credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1
identityProvider:identityProvider
unauthRoleArn:nil
authRoleArn:nil];
configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1
credentialsProvider:self.credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
[[credentialsProvider refresh] continueWithBlock:^id(BFTask *task){
[self testAuth];
return nil;
}];
我的 DeveloperAuthenticatedIdentityProvider 代码(BusytimeAuthenticated):
#import "BusytimeAuthenticated.h"
@interface BusytimeAuthenticated()
@property (strong, atomic) NSString *providerName;
@property (strong, atomic) NSString *token;
@end
@implementation BusytimeAuthenticated
@synthesize providerName=_providerName;
@synthesize token=_token;
- (instancetype)initWithRegionType:(AWSRegionType)regionType
identityId:(NSString *)identityId
identityPoolId:(NSString *)identityPoolId
logins:(NSDictionary *)logins
providerName:(NSString *)providerName{
if (self = [super initWithRegionType:regionType identityId:identityId accountId:nil identityPoolId:identityPoolId logins:logins]) {
self.providerName = providerName;
}
return self;
}
// Return the developer provider name which you choose while setting up the
// identity pool in the Amazon Cognito Console
- (BOOL)authenticatedWithProvider {
return [self.logins objectForKey:self.providerName] != nil;
}
// If the app has a valid identityId return it, otherwise get a valid
// identityId from your backend.
- (BFTask *)getIdentityId {
// already cached the identity id, return it
if (self.identityId) {
return [BFTask taskWithResult:nil];
}
// not authenticated with our developer provider
else if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}
// authenticated with our developer provider, use refresh logic to get id/token pair
else {
return [[BFTask taskWithResult:nil] continueWithBlock:^id(BFTask *task) {
if (!self.identityId) {
return [self refresh];
}
return [BFTask taskWithResult:self.identityId];
}];
}
}
// Use the refresh method to communicate with your backend to get an
// identityId and token.
- (BFTask *)refresh {
if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}else{
// KeychainWrapper *keychain = [[KeychainWrapper alloc]init];
AWSLambdaInvoker *lambdaInvoker = [AWSLambdaInvoker defaultLambdaInvoker];
NSDictionary *parameters = @{@"username" : @"SOMEUSERNAME",
@"password":@"SOMEENCRYPTEDPASS",
@"isError" : @NO};
NSLog(@"Here");
[[lambdaInvoker invokeFunction:@"login" JSONObject:parameters] continueWithBlock:^id(BFTask* task) {
if (task.error) {
NSLog(@"Error: %@", task.error);
}
if (task.exception) {
NSLog(@"Exception: %@", task.exception);
}
if (task.result) {
self.identityId = [task.result objectForKey:@"IdentityId" ];
self.token = [task.result objectForKey:@"Token" ];
// [keychain mySetObject:[task.result objectForKey:@"Token" ] forKey:@"Token"];
// [keychain mySetObject:[task.result objectForKey:@"IdentityId" ] forKey:@"IdentityId"];
NSLog(@"Result: %@", task.result);
}
return [BFTask taskWithResult:self.identityId];
}];
}
return NULL;
}
@end
问题总结:
不幸的是,当我测试我的新权限时,我从错误中看到:"Unauth_Role/CognitoIdentityCredentials is not authorized to perform: lambda:InvokeFunction"。显然我没有正确切换。我在我的刷新方法中放置了一个断点,以查看它是否被调用。不是。我不太了解如何正确切换。非常感谢任何帮助让它工作的帮助。
注意:虽然我确实做了一个很大的改变,但我去掉了 "DeveloperAuthenticationClient" class 因为我认为没有它我也能做到。
根据问题中的新信息进行更新...
这里有几件事:
1. 避免像 while(!finished)
这样的代码等待异步任务完成。在最好的情况下,这种忙碌等待的方式将在 100% 时消耗 CPU/core 而无用,并且会对电池寿命产生不利影响,并且只会损害应用程序的性能。相反,使用带有块的通知。因为在这种情况下你已经有了一个 AWSTask
,而不是在 [credentialsProvider refresh] continueWithBlock...
的末尾 returning nil
只需在那里调用你的 [self testAuth]
并做去掉 finished/while 代码。
2. 在你的 getIdentityId
实现中,第一个 if 条件检查是否有 identityId,如果有 是 ,它是 returns nil
。我猜你的目标是在成功验证后缓存 identityId 和 return 这样你就不必在每次调用 getIdentityId 时都调用你的后端。如果是这样的话,很确定你想要 return identityId
而不是 nil
3. 我不认为这是你的问题的原因,但会简化事情:只要你在控制台中配置了具有 Auth/UnAuth 角色的身份池,你就不必在以下情况下显式使用它们初始化 AWSCognitoCredentialsProvider
.
一旦这些问题得到解决,如果您仍然遇到问题,请更详细地调试代码并告诉我们如下内容:
刷新方法是否被调用?如果是这样,它进入了你的 if 语句的哪些部分,结果是什么?它是否曾经进入 else 块并调用您的后端身份提供者?它是否成功检索到身份 ID 并 return 它?
如果您更进一步,但开始遇到稍微不同的问题,那么请将此问题标记为已回答,并 post 标记为单独的问题,而不是继续编辑此问题。这将有助于保持清晰(这个 question/answer 变得很长并且已经更改)。
初始 posted question/code... 的原始答案 getIdentity method of the AWSCognitoCredentialsProvider returns an AWSTask (i.e. a BFTask). So you'll need to call something like continueWithBlock 以便实际执行该方法。在上面的第一段代码中,您似乎没有这样做。
根本问题是您正在尝试调用 Lambda 函数(需要凭据)来获取凭据。因为您使用的是 "default" 客户端配置,所以当您的经过开发人员身份验证的客户端返回响应时,它将覆盖用于访问您的 Lambda 函数的凭据。此外,一旦该 id 已转换为经过身份验证,您将无法使用它在未经身份验证的流程中获取凭据,并且需要生成一个新的未经身份验证的 id 才能再次进行身份验证,然后返回到您经过身份验证的 id。
强烈建议您在 Lambda 函数前设置 API Gateway 以消除这种循环依赖。
总体问题: 我在前端使用开发人员身份验证时遇到问题 (iOS)。我知道我的后端会生成正确的令牌和 identityID,但我的刷新方法从未被调用过。我也看过样本,但我对发生的一切感到有点困惑。 流程说明: 目前我有一个带有登录按钮的登录屏幕。用户按下登录按钮,然后我的 api class 获取凭据,加密密码并将其存储在钥匙串中(现在已注释掉,因为它在模拟器上不起作用)。我的 DeveloperAuthenticatedIdentityProvider 称为我的应用程序 BusytimeAuthenticated。我已经完成了所有方法(我正在使用 AWS lambda 和 DynamoDB 对用户进行身份验证)我从未经身份验证的访问开始,它只允许我访问两种方法,即登录和注册。然后我想假设我的身份验证用户允许我调用我的其他方法。
我的API代码:
[AWSLogger defaultLogger].logLevel = AWSLogLevelVerbose;
id<AWSCognitoIdentityProvider> identityProvider = [[BusytimeAuthenticated alloc] initWithRegionType:AWSRegionUSEast1
identityId:nil
identityPoolId:@"SOMEIDENTITYPOOLID"
logins:@{@"SOMEPROVIDERNAME": @"SOMEUSERNAME"}
providerName:@"SOMEPROVIDERNAME"
];
credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1
identityProvider:identityProvider
unauthRoleArn:nil
authRoleArn:nil];
configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1
credentialsProvider:self.credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
[[credentialsProvider refresh] continueWithBlock:^id(BFTask *task){
[self testAuth];
return nil;
}];
我的 DeveloperAuthenticatedIdentityProvider 代码(BusytimeAuthenticated):
#import "BusytimeAuthenticated.h"
@interface BusytimeAuthenticated()
@property (strong, atomic) NSString *providerName;
@property (strong, atomic) NSString *token;
@end
@implementation BusytimeAuthenticated
@synthesize providerName=_providerName;
@synthesize token=_token;
- (instancetype)initWithRegionType:(AWSRegionType)regionType
identityId:(NSString *)identityId
identityPoolId:(NSString *)identityPoolId
logins:(NSDictionary *)logins
providerName:(NSString *)providerName{
if (self = [super initWithRegionType:regionType identityId:identityId accountId:nil identityPoolId:identityPoolId logins:logins]) {
self.providerName = providerName;
}
return self;
}
// Return the developer provider name which you choose while setting up the
// identity pool in the Amazon Cognito Console
- (BOOL)authenticatedWithProvider {
return [self.logins objectForKey:self.providerName] != nil;
}
// If the app has a valid identityId return it, otherwise get a valid
// identityId from your backend.
- (BFTask *)getIdentityId {
// already cached the identity id, return it
if (self.identityId) {
return [BFTask taskWithResult:nil];
}
// not authenticated with our developer provider
else if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}
// authenticated with our developer provider, use refresh logic to get id/token pair
else {
return [[BFTask taskWithResult:nil] continueWithBlock:^id(BFTask *task) {
if (!self.identityId) {
return [self refresh];
}
return [BFTask taskWithResult:self.identityId];
}];
}
}
// Use the refresh method to communicate with your backend to get an
// identityId and token.
- (BFTask *)refresh {
if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}else{
// KeychainWrapper *keychain = [[KeychainWrapper alloc]init];
AWSLambdaInvoker *lambdaInvoker = [AWSLambdaInvoker defaultLambdaInvoker];
NSDictionary *parameters = @{@"username" : @"SOMEUSERNAME",
@"password":@"SOMEENCRYPTEDPASS",
@"isError" : @NO};
NSLog(@"Here");
[[lambdaInvoker invokeFunction:@"login" JSONObject:parameters] continueWithBlock:^id(BFTask* task) {
if (task.error) {
NSLog(@"Error: %@", task.error);
}
if (task.exception) {
NSLog(@"Exception: %@", task.exception);
}
if (task.result) {
self.identityId = [task.result objectForKey:@"IdentityId" ];
self.token = [task.result objectForKey:@"Token" ];
// [keychain mySetObject:[task.result objectForKey:@"Token" ] forKey:@"Token"];
// [keychain mySetObject:[task.result objectForKey:@"IdentityId" ] forKey:@"IdentityId"];
NSLog(@"Result: %@", task.result);
}
return [BFTask taskWithResult:self.identityId];
}];
}
return NULL;
}
@end
问题总结: 不幸的是,当我测试我的新权限时,我从错误中看到:"Unauth_Role/CognitoIdentityCredentials is not authorized to perform: lambda:InvokeFunction"。显然我没有正确切换。我在我的刷新方法中放置了一个断点,以查看它是否被调用。不是。我不太了解如何正确切换。非常感谢任何帮助让它工作的帮助。
注意:虽然我确实做了一个很大的改变,但我去掉了 "DeveloperAuthenticationClient" class 因为我认为没有它我也能做到。
根据问题中的新信息进行更新...
这里有几件事:
1. 避免像 while(!finished)
这样的代码等待异步任务完成。在最好的情况下,这种忙碌等待的方式将在 100% 时消耗 CPU/core 而无用,并且会对电池寿命产生不利影响,并且只会损害应用程序的性能。相反,使用带有块的通知。因为在这种情况下你已经有了一个 AWSTask
,而不是在 [credentialsProvider refresh] continueWithBlock...
的末尾 returning nil
只需在那里调用你的 [self testAuth]
并做去掉 finished/while 代码。
2. 在你的 getIdentityId
实现中,第一个 if 条件检查是否有 identityId,如果有 是 ,它是 returns nil
。我猜你的目标是在成功验证后缓存 identityId 和 return 这样你就不必在每次调用 getIdentityId 时都调用你的后端。如果是这样的话,很确定你想要 return identityId
而不是 nil
3. 我不认为这是你的问题的原因,但会简化事情:只要你在控制台中配置了具有 Auth/UnAuth 角色的身份池,你就不必在以下情况下显式使用它们初始化 AWSCognitoCredentialsProvider
.
一旦这些问题得到解决,如果您仍然遇到问题,请更详细地调试代码并告诉我们如下内容: 刷新方法是否被调用?如果是这样,它进入了你的 if 语句的哪些部分,结果是什么?它是否曾经进入 else 块并调用您的后端身份提供者?它是否成功检索到身份 ID 并 return 它?
如果您更进一步,但开始遇到稍微不同的问题,那么请将此问题标记为已回答,并 post 标记为单独的问题,而不是继续编辑此问题。这将有助于保持清晰(这个 question/answer 变得很长并且已经更改)。
初始 posted question/code... 的原始答案 getIdentity method of the AWSCognitoCredentialsProvider returns an AWSTask (i.e. a BFTask). So you'll need to call something like continueWithBlock 以便实际执行该方法。在上面的第一段代码中,您似乎没有这样做。
根本问题是您正在尝试调用 Lambda 函数(需要凭据)来获取凭据。因为您使用的是 "default" 客户端配置,所以当您的经过开发人员身份验证的客户端返回响应时,它将覆盖用于访问您的 Lambda 函数的凭据。此外,一旦该 id 已转换为经过身份验证,您将无法使用它在未经身份验证的流程中获取凭据,并且需要生成一个新的未经身份验证的 id 才能再次进行身份验证,然后返回到您经过身份验证的 id。
强烈建议您在 Lambda 函数前设置 API Gateway 以消除这种循环依赖。