I'm a newbie on mobile dev. I'm trying to authenticate to Amazon Cognito.
I first login to Credentials Provider using a username, pin, platform and deviceToken using custom services model - I then get identityId, endPoint and token back. I'm told that I need to swap the token I got back and refresh my credentials in order for me to be authenticated to AWS Cognito and S3. But all the process is confusing and have a lot of examples that are different.
I've created a SignInProvider, extending AWSSignInProvider to access the - (void) login: (void (^) (id result, NSError *error)) completionHanlder; I have my token, endpoint and identityId inside my login method..what do I do with the completion handler and whats next after.
#implementation SignInProvider
+(instanceType) sharedInstance{}
- (NSString) identityProviderName{}
- (AWSTask<NSString*>*) token{}
- (BOOL) isLoggedIn{}
- (NSSting*) userName{}
- (void) reloadSession{}
- (void) login: (void (^) (id result, NSError *error)) completionHanlder{
authRequest = [IMPCLDMobileAuthenticationRequest new];
[authRequest setToken:#"930fc1b56d8ca19a84500f9a79af71b65f60331f0242ce4395cdf41186443692"];
[authRequest setPassword:#"pin"];
[authRequest setUsername:#"example#email.co.za"];
[authRequest setPlatform:#"ios"];
serviceClient = [IMPCLDImpressionInternalMicroserviceClient defaultClient];
[[serviceClient mobileAuthenticationPost:authRequest] continueWithBlock:^id(AWSTask *loginTask)
{
//what to do here with my loginTask results (token, endpoint, identityId)
}
return nil;
}
To swap/save token in AWS you need to do below in your continueWithBlock
[[serviceClient mobileAuthenticationPost:authRequest] continueWithBlock:^id(AWSTask *loginTask)
{
AWSSNSCreateEndpointResponse *response = loginTask.result;
AWSSNSSubscribeInput *subscribeRequest = [AWSSNSSubscribeInput new];
subscribeRequest.endpoint = response.endpointArn;
subscribeRequest.protocols = #"application";
subscribeRequest.topicArn = YOUR_TOPIC_ARN;
return [sns subscribe:subscribeRequest];
}] continueWithBlock:^id(AWSTask *task) {
if (task.cancelled) {
NSLog(#"Task cancelled");
}
else if (task.error) {
NSLog(#"Error occurred: [%#]", task.error);
}
else {
NSLog(#"Success");
}
return nil;
}];
Related
When I try to login to YouTube and upload a video, it gets uploaded without any issue. If I upload a video after 2-3 hours, I will get an error saying,
Error: Error Domain=com.google.GTLJSONRPCErrorDomain Code=401 "The operation couldn’t be completed. (Invalid Credentials)" UserInfo=0x14585d90 {error=Invalid Credentials, GTLStructuredError=GTLErrorObject 0x14d85ba0: {message:"Invalid Credentials" code:401 data:[1]}, NSLocalizedFailureReason=(Invalid Credentials)}
Here is the code which does Youtube login,
GIDSignIn *googleSignIn = [GDSharedInstance googleSDKApplicationSharedInstance];
googleSignIn.delegate = self;
googleSignIn.scopes = [NSArray arrayWithObject:#"https://www.googleapis.com/auth/youtube"];
[googleSignIn signIn];
signin delegate
- (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error
{
// Auth is converted to use it for uploading a video
GTMOAuth2Authentication *youTubeAuth = [[GTMOAuth2Authentication alloc] init];
youTubeAuth.clientID = kClientID;
youTubeAuth.clientSecret = #"xxx";
youTubeAuth.userEmail = googleUser.profile.email;
youTubeAuth.userID = googleUser.userID;
youTubeAuth.accessToken = googleUser.authentication.accessToken;
youTubeAuth.refreshToken = googleUser.authentication.refreshToken;
youTubeAuth.expirationDate = googleUser.authentication.accessTokenExpirationDate;
self.youTubeService.authorizer = youTubeAuth;
}
Upload code,
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:[NSURL URLWithString:path] error:&error];
if (fileHandle) {
NSString *mimeType = [self MIMETypeForFilename:filename
defaultMIMEType:#"video/mov"];
GTLUploadParameters *uploadParameters =
[GTLUploadParameters uploadParametersWithFileHandle:fileHandle
MIMEType:mimeType];
uploadParameters.uploadLocationURL = locationURL;
GTLQueryYouTube *query = [GTLQueryYouTube queryForVideosInsertWithObject:video
part:#"snippet,status,recordingDetails"
uploadParameters:uploadParameters];
GTLServiceYouTube *service = self.youTubeService;
self.uploadFileTicket = [service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLYouTubeVideo *uploadedVideo,
NSError *error)
{
// here I will get 401 error
}];
}
The only problem is with the GTLServiceYouTube. GIDSignIn seems to handle the refresh tokens, so that the user is always logged in after the first login. But the GTLOAuth2Authentication only works on the first login and is broken after one hour.
The token needs to be refreshed.
Use this piece of code :-
- (void)applicationWillEnterForeground:(UIApplication *)application {
[[GIDSignIn sharedInstance] signInSilently]
}
The problem here is that your auth token is expiring. You will have to use your refresh token to get a new, valid auth token after your old token expires.
If you are using an older version of the Google Plus iOS SDK, You can use GTMOAuth2Authentication to force a refresh of the auth token with the authorizeRequest: method.
From GTMOAuth2Authentication.h
// The request argument may be nil to just force a refresh of the access token,
// if needed.
- (void)authorizeRequest:(NSMutableURLRequest *)request
completionHandler:(void (^)(NSError *error))handler;
Implementation:
// In your sign in method
[[GPPSignIn sharedInstance] setKeychainName:#"googleAuth"];
// ...
// Retrieving auth and refreshing token
GTMOAuth2Authentication *auth;
auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:#"googleAuth"
clientID:#"kYourGoogleClientId"
clientSecret:#"kYourGoogleClientSecret"];
NSLog(#"old auth: %#", auth);
[auth authorizeRequest:nil completionHandler:^(NSError *error) {
if (error) {
// no auth data or refresh failed
NSLog(#"Error: %#", error);
} else {
// Auth token refresh successful
NSLog(#"new auth: %#", auth);
}
}];
I am creating an iOS application in which I am integrating Office-365-SDK-for-iOS for import contacts from outlook365.I am successfully able to authenticate with Microsoft Azure. But when I am fetching user and user's contacts then following error is coming-
Error Domain=Error in the Request Code=401 "The operation couldn’t be completed. (Error in the Request error 401.)
Here is my code for authentication and get client
//Acquire access and refresh tokens from Azure AD for the user.
-(void)acquireAuthTokenWithResourceId:(NSString *)resourceId completionHandler:(void (^)(BOOL authenticated))completionBlock
{
ADAuthenticationError *error;
self.authContext = [ADAuthenticationContext authenticationContextWithAuthority:OutlookAuthority error:&error];
[self.authContext acquireTokenWithResource:OutlookRsourceId
clientId:OutlookClientId
redirectUri:[NSURL URLWithString:OutlookRedirectUrl]
completionBlock:^(ADAuthenticationResult *result)
{
if (AD_SUCCEEDED != result.status)
{
completionBlock(NO);
}
else
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:result.tokenCacheStoreItem.userInformation.userId
forKey:#"LogInUser"];
[userDefaults synchronize];
self.dependencyResolver = [[ADALDependencyResolver alloc] initWithContext:self.authContext
resourceId:OutlookRsourceId
clientId:OutlookClientId
redirectUri:[NSURL URLWithString:OutlookRedirectUrl]];
completionBlock(YES);
}
}];
}
- (void) getClient:(void (^) (MSOutlookServicesClient *))callback
{
OutlookAuthManager* authenticationController = [OutlookAuthManager sharedInstance];
[authenticationController acquireAuthTokenWithResourceId:OutlookRsourceId completionHandler:^(BOOL authenticated)
{
if (authenticated)
{
callback([[MSOutlookServicesClient alloc] initWithUrl:#"https://outlook.office365.com/api/v1.0" dependencyResolver:[authenticationController dependencyResolver]]);
}
else
{
NSLog(#"Error in authentication");
}
}];
}
And following I am getting user-
[[OutlookAuthManager sharedInstance] getClient:^(MSOutlookServicesClient *client)
{
NSURLSessionTask* task = [[client getMe] readWithCallback:^(MSOutlookServicesUser *user, NSError *error)
{
if(error == nil)
{
dispatch_async(dispatch_get_main_queue(),
^{
NSLog(#"------>%#",user.DisplayName);
NSLog(#"------>%#",user.Alias);
NSLog(#"------>%#",user.Id);
NSLog(#"------>%#",user.MailboxGuid);
});
}
else
{
[client.resolver.logger logMessage:error.description withLevel:LOG_LEVEL_ERROR];
}
}];
[task resume];
}];
But That error is coming here.
Please help me
Thanks
Start by validating your access token. Since it's a 401 it's likely a problem there.
I am currently unable to authorize users using AWS iOS SDK V2 using Facebook and Google+ as the provider.
I'm not sure if its my setup on the AWS Developer Console, or whether its the code.
This is the role policy for the identity pools:
{
"Version": "2012-10-17",
"Statement": [{
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*"
],
"Effect": "Allow",
"Resource": ["*"]
}]
I do receive an unauthorized Cognito ID but when I try to use either Facebook or Google+ provider authentication, it does not work.
Once the Facebook login returns I can successfully use the user properties to extract the profile picture, name and email address. I then get the token (yes it is a very long string of characters) from the Facebook session and use it in the deviceID class:
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
//Populate viewcontoller with Facebook data
self.profilePictureView.profileID = user.id;
NSRange range = [user.name rangeOfString:#" "];
self.firstName.text = [user.name substringToIndex:range.location];
self.lastName.text = [user.name substringFromIndex:range.location+1];
self.emailAddress.text = [user objectForKey:#"email"];
//Get Facebook token, set then get Cognito device ID - in DeviceId class
NSString *token = FBSession.activeSession.accessTokenData.accessToken;
DeviceId *myDeviceId = [DeviceId sharedInstance];
cognitoDeviceId = [myDeviceId setFacebookToken:token];
}
The DeviceID class implementation is shown below:
#import "DeviceId.h"
#import <AWSiOSSDKv2/AWSCore.h>
#import <AWSCognitoSync/Cognito.h>
#implementation DeviceId
static NSString *cognitoId;
static DeviceId *_sharedInstance;
static AWSCognitoCredentialsProvider *credentialsProvider;
static AWSServiceConfiguration *configuration;
+ (DeviceId *) sharedInstance
{
if (!_sharedInstance)
{
_sharedInstance = [[DeviceId alloc] init];
}
return _sharedInstance;
}
- (NSString *) getDeviceId
{
return cognitoId;
}
- (void) setDeviceId
{
/*
* AWS Cognito
*/
credentialsProvider = [AWSCognitoCredentialsProvider
credentialsWithRegionType:AWSRegionUSEast1
accountId:#"(accountID"
identityPoolId:#"(identityPool)"
unauthRoleArn:#"arn:aws:iam::(accountID):role/Cognito_(app)UsersUnauth_DefaultRole"
authRoleArn:#"arn:aws:iam::(accountID):role/Cognito_(app)UsersAuth_DefaultRole"];
configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1
credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
// Retrieve the cognito ID.
cognitoId = credentialsProvider.identityId;
if (!cognitoId) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Identification Error"
message:#"Error on User Account."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
-(NSString *)setFacebookToken:(NSString*)token {
credentialsProvider.logins = #{ #(AWSCognitoLoginProviderKeyFacebook): token };
[self setDeviceId];
return cognitoId;
}
-(NSString *)setGooglePlusToken:(NSString*)token {
credentialsProvider.logins = #{ #(AWSCognitoLoginProviderKeyGoogle): token };
[self setDeviceId];
return cognitoId;
}
#end
I get no error message and the dashboard above never shows an authenticated user. The CognitoID never changes its value. Can someone tell me where the issue is?
EDIT: Updated DeviceId.m based on comments still returns nil for cognitoId
EDIT 2: Updated DeviceId.m to replace while loop checking if BFTask was finished to Bolts method waitUntilFinished.
#import "DeviceId.h"
#import <AWSiOSSDKv2/AWSCore.h>
#import <AWSCognitoSync/Cognito.h>
#implementation DeviceId
{
__block NSString *tempCognitoId;
}
static NSString *cognitoId;
static DeviceId *_sharedInstance;
static AWSCognitoCredentialsProvider *credentialsProvider;
static AWSServiceConfiguration *configuration;
+ (DeviceId *) sharedInstance
{
if (!_sharedInstance)
{
_sharedInstance = [[DeviceId alloc] init];
}
return _sharedInstance;
}
- (NSString *) getDeviceId
{
return cognitoId;
}
- (void) setDeviceId
{
/*
* AWS Cognito
*/
credentialsProvider = [AWSCognitoCredentialsProvider
credentialsWithRegionType:AWSRegionUSEast1
identityPoolId:#"Identity Pool ID"];
configuration = [AWSServiceConfiguration
configurationWithRegion:AWSRegionUSEast1
credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
BFTask *taskIdentity = [[credentialsProvider refresh] continueWithBlock:^id(BFTask *task){
if (task.error == nil)
{
tempCognitoId = credentialsProvider.identityId;
NSLog(#"cognitoId: %#", cognitoId);
}
else
{
NSLog(#"Error : %#", task.error);
}
return nil;
}];
[taskIdentity waitUntilFinished];
cognitoId = tempCognitoId;
}
-(NSString *)setFacebookToken:(NSString*)token {
credentialsProvider.logins = #{ #(AWSCognitoLoginProviderKeyFacebook): token };
BFTask *taskIdentity = [[credentialsProvider refresh] continueWithBlock:^id(BFTask *task){
if (task.error == nil)
{
tempCognitoId = credentialsProvider.identityId;
NSLog(#"cognitoId: %#", tempCognitoId);
}
else
{
NSLog(#"Error : %#", task.error);
}
return nil;
}];
[taskIdentity waitUntilFinished];
cognitoId = tempCognitoId;
return cognitoId;
}
-(NSString *)setGooglePlusToken:(NSString*)token {
credentialsProvider.logins = #{ #(AWSCognitoLoginProviderKeyGoogle): token };
BFTask *taskIdentity = [[credentialsProvider getIdentityId] continueWithBlock:^id(BFTask *task){
if (task.error == nil)
{
tempCognitoId = credentialsProvider.identityId;
NSLog(#"cognitoId: %#", tempCognitoId);
}
else
{
NSLog(#"Error : %#", task.error);
}
return nil;
}];
[taskIdentity waitUntilFinished];
cognitoId = tempCognitoId;
return cognitoId;
}
#end
I do realize that some may consider waiting for completion is bad practice but I need to be sure for testing purposes that cognitoId is returned "synchronously". After modifying the Facebook App ID, using this method the cognitoID is returned.
Edit 3: However, Google+ is failing before reaching deviceId.
- (void)finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
if (error) {
_signInAuthStatus.text =
[NSString stringWithFormat:#"Status: Authentication error: %#", error];
NSLog(#"%#", error);
return;
}
NSString *idToken = [auth.parameters objectForKey:#"id_token"];
DeviceId *myDeviceId = [DeviceId sharedInstance];
[myDeviceId setGooglePlusToken:idToken];
}
The NSLog error prints: Error Domain=com.google.GooglePlusPlatform Code=-1 "The operation couldn’t be completed. (com.google.HTTPStatus error 400.)" The API & Auth Product Name and Current Email Address appear to be correct on the console.
Edit 4: Cognito Synch in another section of the code has now stopped working where it worked before:
AWSCognito *syncClient = [AWSCognito defaultCognito];
AWSCognitoDataset *dataset = [syncClient openOrCreateDataset:#"myDataSet"];
NSString *fullName = [dataset stringForKey:#"name"];
Fails on the first line with the error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[AWSEndpoint endpointWithRegion:service:]: unrecognized selector sent to class
Any help on these additional errors is appreciated.
A common cause for the issue you described with
Error Domain=com.google.GooglePlusPlatform Code=-1 "The operation couldn’t be completed. (com.google.HTTPStatus error 400.)
Is described in this stackoverflow question. I'd double check that you have the product name and email address entered. Additionally, I believe you have to re-create the OAuth credentials after entering this data, so if you hadn't yet done that, it may be worth trying.
Finally, check that you're using the web server API key, not the iOS client key.
It looks like, after setting the token in the credentialsProvider.logins map, you are overriding your credentialsProvider by calling setDeviceId again. setDeviceId creates a brand new AWSCognitoCredentialsProvider, which will not have the token you added to the previous one, and overwrites the variable credentialsProvider.
I'm trying to use Google's OAuth2 and YouTube APIs. The OAuth returns GTMOAuth2Authentication object that you then use to make requests to services like YouTube. My login works fine, and when I manually pass the authentication object, I can make requests.
However, I should also be able to access the authentication object via keychain, and I receive a valid object, but if I try to use it to make requests, I cannot. I keep getting the following error: "The operation couldn’t be completed. (com.google.GTMHTTPFetcher error -1.)" I'd appreciate it if someone could point out my mistake. I'm testing on a real iPhone 5s.
Authentication Code:
GTMOAuth2ViewControllerTouch * viewController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:scope
clientID:kGoogleClientID
clientSecret:kGoogleClientSecret
keychainItemName:kGoogleKeychainItemName
delegate:self
finishedSelector:#selector(viewController:
finishedWithAuth:
error:)];
[self.navigationController pushViewController:viewController animated:YES];
Authentication Completion Handler:
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
NSLog(#"%s", __PRETTY_FUNCTION__);
if (error != nil) {
NSLog(#"%s %#", __PRETTY_FUNCTION__, error.localizedDescription);
return;
}
MediaGETWrapper *getWrapper = [MediaGETWrapper sharedWrapper];
getWrapper.googleAuth = auth; // passing auth directly without keychain
[getWrapper youTubeSubscriptionsWithSuccess:nil failure:nil];
YouTube Client Initialization:
self.youTube = [GTLServiceYouTube new];
GTMOAuth2Authentication *auth = [GTMOAuth2Authentication new];
[GTMOAuth2ViewControllerTouch
authorizeFromKeychainForName:kGoogleKeychainItemName
authentication:auth
error:nil];
self.youTube.authorizer = auth;
Request:
- (void)youTubeSubscriptionsWithSuccess:(void(^)(NSArray *subscriptions))success
failure:(void(^)(NSError *error))error {
NSLog(#"%s", __PRETTY_FUNCTION__);
// self.youTube.authorizer = self.googleAuth; // If uncommented, works!
GTLQueryYouTube *query = [GTLQueryYouTube queryForSubscriptionsListWithPart:#"snippet"];
query.mine = YES;
[self.youTube
executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLYouTubeChannelListResponse *channelList,
NSError *error) {
if (error != nil) {
NSLog(#"%s %#", __PRETTY_FUNCTION__, error.localizedDescription); // fails here
return;
}
for (GTLYouTubeSubscription *channel in channelList) {
NSLog(#"%#", channel.snippet.title);
}
}];
}
I couldn't find a direct fix, but you can do this:
Get the access token from a GTMOAuth2Authentication object via:
auth.accessToken
Then set the access token wherever you want to make requests.
To refresh the token, use this method:
[auth
authorizeRequest:nil // just to refresh
completionHandler:^(NSError *error) {
// your code here
}];
I am using "STTwitter" for getting token from below URL and request body
https://dev.twitter.com/docs/api/1.1/post/oauth2/token
request body code with URL is below
- (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBlock errorBlock:(void(^)(NSError *error))errorBlock {
[self postResource:#"oauth2/token"
baseURLString:#"https://api.twitter.com"
parameters:#{ #"grant_type" : #"client_credentials" }
useBasicAuth:YES
uploadProgressBlock:nil
downloadProgressBlock:nil
successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) {
NSString *tokenType = [json valueForKey:#"token_type"];
if([tokenType isEqualToString:#"bearer"] == NO) {
NSError *error = [NSError errorWithDomain:NSStringFromClass([self class]) code:STTwitterAppOnlyCannotFindBearerTokenInResponse userInfo:#{NSLocalizedDescriptionKey : #"Cannot find bearer token in server response"}];
errorBlock(error);
return;
}
self.bearerToken = [json valueForKey:#"access_token"];
successBlock(_bearerToken);
} errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) {
errorBlock(error);
NSLog(#"ERROR %#",[error description]);
}];
}
For Calling above method i am doing below code
STTwitterAppOnly *twitter1 = [[STTwitterAppOnly alloc] init];
[twitter1 verifyCredentialsWithSuccessBlock:^(NSString *bearerToken) {
NSLog(#"sd");
[self.twitter getUserTimelineWithScreenName:#"SEREEN_NAME"
successBlock:^(NSArray *statuses) {
NSLog(#"ERROR ::: %#",[statuses description]);
// ...
} errorBlock:^(NSError *error) {
// ...
}];
} errorBlock:^(NSError *error) {
// ...
}];
I got below Error when perform above code...
**Error Domain=STHTTPRequest Code=99 "Unable to verify your credentials"**
Can you suggest me what I missed in my code?
My concern is that, I want to read twitter feed without login in Twitter.Only using "Consumer Key" and "Secret Key" with Twitter API V1.1.
Thanks
if you have ConsumerKey and ConsumerSecret you can try it
STTwitterAPIWrapper *twitterAPI = [[STTwitterAPIWrapper twitterAPIApplicationOnlyWithConsumerKey:#"Your Consumer Key" consumerSecret:#"Your Consumer Secret"] autorelease];
[twitterAPI verifyCredentialsWithSuccessBlock:^(NSString *username) {
[twitterAPI getUserTimelineWithScreenName:#"your ScreenName" count:25 successBlock:^(NSArray *statuses) {
NSLog(#"Success:%#", statuses);
} errorBlock:^(NSError *error){
NSLog(#"Error : %#",error.description);
}];
} errorBlock:^(NSError *error) {
NSLog(#"Error : %#",error.description);
}];
Verify that you have enter Consumer key and Consumer Secret in both plist and header prefix...
The code you posted has several issues. First, you're making a request from the verify method error block instead of the success block. Second, you're using the postResource method directly, instead of the higher level one to do what you want.
If all that you want is a bearer token for your consumer tokens, you can just copy / paste the sample code from STTwitter README in the App Only Authentication section.