The app is not getting successful API call after hours of inactivity.
Steps to reproduce the behavior:
Creation of Amazon Cognito ID.
API calls.
Put the app in the background.
Refresh code after 8 hours. (initializeAmazonCongnitoProviderWithCompletionBlock mentioned below is called first when the app comes to foreground only after 8 hours)
Missing auth token will appear as suggested in the screenshot.
Which AWS service(s) are affected?
- (void)initializeAmazonCognitoProviderWithCompletionBlock:(void (^)(void))completion {
[[UIApplication sharedApplication].keyWindow setUserInteractionEnabled:FALSE];
NSString* AWSCognitoPoolID = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"AWSCognitoID"];
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AMAZON_COGNITO_REGION identityPoolId:AWSCognitoPoolID];
[credentialsProvider clearCredentials];
AWSServiceConfiguration *configuration =[[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
[self getCognitoID:credentialsProvider CompletionBlock:^{
[self expirationHandler:credentialsProvider CompletionBlock:^{
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication].keyWindow setUserInteractionEnabled:TRUE];
});
completion();
}];
}];
}
- (void)expirationHandler:(AWSCognitoCredentialsProvider *)creds CompletionBlock: (void (^)(void))completion {
[[creds credentials] continueWithBlock:^id(AWSTask *task) {
if (task.error) {
[self initializeAmazonCognitoProviderWithCompletionBlock:^{}];
} else {
AWSCredentials *cred = (AWSCredentials*) task.result;
NSDateFormatter *dateFormat = [[NSDateFormatter alloc]init];
[dateFormat setDateFormat:#"yyyy-MM-dd'T'HH:mm:ssZ"];
/* https://aws.amazon.com/premiumsupport/knowledge-center/security-token-expired/
https://forums.aws.amazon.com/thread.jspa?threadID=166398
We should fire timer before 5 minutes of expiration.
NSString *expF = [dateFormat stringFromDate:cred.expiration];
*/
[NSTimer scheduledTimerWithTimeInterval:cred.expiration.timeIntervalSinceNow - 300 target:self selector:#selector(initializeAmazonCognitoProviderWithCompletionBlock:) userInfo:nil repeats:NO];
completion();
}
return nil;
}];
}
- (void)getCognitoID:(AWSCognitoCredentialsProvider *)creds CompletionBlock: (void (^)(void))completion {
[[creds getIdentityId] continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"Error: %#", task.error);
[self initializeAmazonCognitoProviderWithCompletionBlock:^{}];
} else {
NSString *cognitoId = task.result;
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
if (![[standardDefaults valueForKey:#"UserCognitoID"] isEqualToString:cognitoId]) {
[standardDefaults setObject:#"" forKey:BainPreferenceToken];
[standardDefaults setInteger:0 forKey:#"registrationPostComplete"];
}
[standardDefaults setObject:cognitoId forKey:#"UserCognitoID"];
[standardDefaults synchronize];
completion();
}
return nil;
}];
}
AWSCognitoCredentialsProvider is an object that retrieves the credentials from Amazon Cognito and supplies credentials to sign the requests to AWS (for example to upload file to S3).
AWSCognitoIdentityProvider object in the SDK provides the abstraction for Amazon Cognito UserPools and helps you sign-in a user and retrieve the tokens that proves that the user is authenticated.
When a user is signed-in and AWSCognitoIdentityProvider has retrieved the tokens from Amazon Cognito UserPools, the tokens can be federated into the AWSCognitoCredentialsProvider in order to retrieve AWSCredentials and Cognito Identity Id for this authenticated user. To do so, you need to supply the token in the logins map and pass the logins map to AWSCognitoCredentialsProvider.
To accomplish this, you need to
1) create a class that confirms to the AWSIdentityProviderManager protocol and implements the logins method that returns a map of { providerLoginKey -> token }.
#interface AWSFederationManager : NSObject<AWSIdentityProviderManager>
#property (nonatomic, readonly, strong) AWSCognitoCredentialsProvider *credentialsProvider;
#end
#interface AWSFederationManager()
#property (nonatomic, readwrite, strong) AWSCognitoCredentialsProvider *credentialsProvider;
#end
#implementation AWSFederationManager
- (AWSTask<NSDictionary<NSString *, NSString *> *> *)logins {
return [AWSTask taskWithResult:#{[<identityProviderName> : <token>}];
}
#end
2) Set the delegate of the AWSCognitoCredentialsProvider object to the class created in step (1).
#implementation AWSFederationManager
- (instancetype) initialize {
[self.credentialsProvider setIdentityProviderManagerOnce:self];
}
#end
Now once the federation is successful, you will be able to retrieve the AWSCredentials and Cognito Identity Id.
You can refer Source for the reference implementation.
Related
I am following the Spotify SDK tutorial, and trying to make a RN module for my application. This is my SpotifyModule.m code:
#import "SpotifyModule.h"
#import "React/RCTLog.h"
#import "React/RCTBridge.h"
#implementation SpotifyModule
RCT_EXPORT_MODULE()
+ (id)sharedManager {
static SpotifyModule *sharedManager = nil;
#synchronized(self) {
if (sharedManager == nil)
sharedManager = [[self alloc] init];
}
return sharedManager;
}
RCT_EXPORT_METHOD(authenticate:(RCTResponseSenderBlock)callback)
{
// Your implementation here
RCTLogInfo(#"authenticate");
self.auth = [SPTAuth defaultInstance];
// The client ID you got from the developer site
self.auth.clientID = #"8fff6cbb84d147e383060be62cec5dfa";
// The redirect URL as you entered it at the developer site
self.auth.redirectURL = [NSURL URLWithString:#"my-android-auth://callback"];
// Setting the `sessionUserDefaultsKey` enables SPTAuth to automatically store the session object for future use.
self.auth.sessionUserDefaultsKey = #"current session";
// Set the scopes you need the user to authorize. `SPTAuthStreamingScope` is required for playing audio.
self.auth.requestedScopes = #[SPTAuthPlaylistReadPrivateScope, SPTAuthUserReadPrivateScope];
//save the login callback
SpotifyModule *spotifyModule = (SpotifyModule *)[SpotifyModule sharedManager];
spotifyModule.loginCallback = callback;
//setup event dispatcher
spotifyModule.eventDispatcher = [[RCTEventDispatcher alloc] init];
[spotifyModule.eventDispatcher setValue:self.bridge forKey:#"bridge"];
// Start authenticating when the app is finished launching
dispatch_async(dispatch_get_main_queue(), ^{
[self startAuthenticationFlow];
});
}
- (void)startAuthenticationFlow
{
// Check if we could use the access token we already have
if ([self.auth.session isValid]) {
// Use it to log in
SpotifyModule *spotifyModule = (SpotifyModule *)[SpotifyModule sharedManager];
NSString *accessToken = self.auth.session.accessToken;
spotifyModule.loginCallback(#[accessToken]);
} else {
// Get the URL to the Spotify authorization portal
NSURL *authURL = [self.auth spotifyWebAuthenticationURL];
// Present in a SafariViewController
self.authViewController = [[SFSafariViewController alloc] initWithURL:authURL];
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootViewController presentViewController:self.authViewController animated:YES completion:nil];
}
}
- (BOOL) application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary *)options
{
// If the incoming url is what we expect we handle it
if ([self.auth canHandleURL:url]) {
// Close the authentication window
[self.authViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
self.authViewController = nil;
// Parse the incoming url to a session object
[self.auth handleAuthCallbackWithTriggeredAuthURL:url callback:^(NSError *error, SPTSession *session) {
if (session) {
// Send auth token
SpotifyModule *spotifyModule = (SpotifyModule *)[SpotifyModule sharedManager];
NSString *accessToken = session.accessToken;
spotifyModule.loginCallback(#[accessToken]); }
}];
return YES;
}
return NO;
}
#end
The way I want to use it from the RN end, is call authenticate, with a callback for the access token. I got this working on Android fine.
Native.authenticate(function(token) {
store.dispatch(actions.loginSuccess(token));
});
On iOS, with the above code, I get to the attached screen, and when clicking Ok I get the following error:
SpotiFind[5475:29641] *** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '+[SpotifyModule
application:openURL:sourceApplication:annotation:]: unrecognized
selector sent to class 0x10cb406f8'
So from my minimal ObjectiveC understanding, its trying to call a different method, than the one that the tutorial instructs to implement.
Any recommendations on how to make this work ?
If its any relevant, I build against iOS 10, and use the latest Spotify iOS SDK
p.s I realize the name might be against some copyrighting, its just temp for development :)
Thanks to your tips (in the comments), we managed to make our Spotify authentication work with React-native.
We used the code from your Pastebin to create a reusable module so that nobody has to waste time anymore.
You can find the module here: emphaz/react-native-ios-spotify-sdk
There is a tutorial for the setup and we even created a boilerplate project
Thanks a lot Giannis !
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 am trying to authorize Twitter For a user in iOS and Temboo. Here is how I am attempting to do it.
In my SetttingsViewController class I have a button where the user can tap to begin the TwitterAuthentication. I have the InitializeOauth methods in a class called TwitterClient, and the FinalizeOauth methods in a class called TwitterFClient. Here are my classes.
TwitterClient.m
-(void)runInitializeOAuthChoreo {
// Instantiate the Choreo, using a previously instantiated TembooSession object, eg:
TMBTembooSession *session = [[TMBTembooSession alloc] initWithAccount:#"prnk28" appKeyName:#"Floadt" andAppKeyValue:#"9b9031d182d7441da05f8214ba2c7170"];
// Create the choreo object using your Temboo session
TMBTwitter_OAuth_InitializeOAuth *initializeOAuthChoreo = [[TMBTwitter_OAuth_InitializeOAuth alloc] initWithSession:session];
// Get Inputs object for the choreo
TMBTwitter_OAuth_InitializeOAuth_Inputs *initializeOAuthInputs = [initializeOAuthChoreo newInputSet];
// Set credential to use for execution
[initializeOAuthInputs setCredential:#"Twitter"];
// Set inputs
[initializeOAuthInputs setForwardingURL:#"floadt://success"];
// Execute choreo specifying this class as the choreo delegate
[initializeOAuthChoreo executeWithInputs:initializeOAuthInputs delegate:self];
}
// TMBChoreographyDelegate method implementation - handle choreo errors
-(void)choreographyDidFailWithError:(NSError*)error {
// Log error to the console
NSLog(#"Error - %#", error);
}
// TMBChoreographyDelegate method implementation - choreo executed successfully
-(void)choreographyDidFinishExecuting:(TMBTwitter_OAuth_InitializeOAuth_ResultSet*)result {
// Log results to the console
// NSLog(#"%#", [result getAuthorizationURL]);
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[result getAuthorizationURL]]];
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
NSString *callbackid = [result getCallbackID];
[user setObject:callbackid forKey:#"TwitterCallbackID"];
[user synchronize];
NSString *oauthtokensecret = [result getOAuthTokenSecret];
[user setObject:oauthtokensecret forKey:#"TwitterTemporaryOAuth"];
[user synchronize];
}
TwitterFClient.m
-(void)runFinalizeOAuthChoreo {
// Instantiate the Choreo, using a previously instantiated TembooSession object, eg:
TMBTembooSession *session = [[TMBTembooSession alloc] initWithAccount:#"prnk28" appKeyName:#"Floadt" andAppKeyValue:#"9b9031d182d7441da05f8214ba2c7170"];
// Create the choreo object using your Temboo session
TMBTwitter_OAuth_FinalizeOAuth *finalizeOAuthChoreo = [[TMBTwitter_OAuth_FinalizeOAuth alloc] initWithSession:session];
// Get Inputs object for the choreo
TMBTwitter_OAuth_FinalizeOAuth_Inputs *finalizeOAuthInputs = [finalizeOAuthChoreo newInputSet];
// Set credential to use for execution
[finalizeOAuthInputs setCredential:#"Twitter"];
// Set inputs
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
NSString *tCall = [user stringForKey:#"TwitterCallbackID"];
NSString *tTAuth = [user stringForKey:#"TwitterTemporaryOAuth"];
[finalizeOAuthInputs setOAuthTokenSecret:tCall];
[finalizeOAuthInputs setCallbackID:tTAuth];
[finalizeOAuthInputs setConsumerKey:TWITTER_CONSUMER_KEY];
[finalizeOAuthInputs setConsumerSecret:TWITTER_CONSUMER_SECRET];
// Execute choreo specifying this class as the choreo delegate
[finalizeOAuthChoreo executeWithInputs:finalizeOAuthInputs delegate:self];
}
// TMBChoreographyDelegate method implementation - handle choreo errors
-(void)choreographyDidFailWithError:(NSError*)error {
// Log error to the console
NSLog(#"Error - %#", error);
}
// TMBChoreographyDelegate method implementation - choreo executed successfully
-(void)choreographyDidFinishExecuting:(TMBTwitter_OAuth_FinalizeOAuth_ResultSet*)result {
// Log results to the console
NSLog(#"%#", [result getAccessTokenSecret]);
NSLog(#"%#", [result getAccessToken]);
NSLog(#"%#", [result getScreenName]);
NSLog(#"%#", [result getUserID]);
}
Those are the two client classes. I believe that in the Facebook example they are both combined would that be the suggested practice?
Anyway this is how the methods are called from within the SettingsViewController Class:
SettingsViewController.m
TwitterClient *twitter = [[TwitterClient alloc] init];
[twitter runInitializeOAuthChoreo];
When the user returns from the Web Browser is when I call the FinalizeOAuth Method. Here is how it is stated in the AppDelegate.
AppDelegate.m
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
NSString *daURL = [url absoluteString];
// NSString *instagram;
NSString *twitter;
twitter = [daURL substringWithRange: NSMakeRange (0, 16)];
// instagram = [daURL substringWithRange:NSMakeRange(0, 27)];
if ([daURL isEqual: #"floadt://success"]) {
TwitterFClient *fo = [[TwitterFClient alloc] init];
[fo runFinalizeOAuthChoreo];
}else{
[[InstagramClient sharedClient] handleOAuthCallbackWithURL:url];
}
return YES;
}
Im under the assumption that there is a much simpler way to do this and I would be open to drastic code change!
It looks like you have these values reversed:
[finalizeOAuthInputs setOAuthTokenSecret:tCall];
[finalizeOAuthInputs setCallbackID:tTAuth];
Also, in the finalize step you're specifying your Twitter credential, but then you're also doing this:
[finalizeOAuthInputs setConsumerKey:TWITTER_CONSUMER_KEY];
[finalizeOAuthInputs setConsumerSecret:TWITTER_CONSUMER_SECRET];
That's unnecessary, since those values are stored in your credential (as evidenced by your use of it in the initialize-oauth flow). You can remove those two lines altogether. If they for some reason didn't match, your choreo execution would fail.
Using AFNetworking, I'm able to successfully login without any problems and store a session. As soon as I stop debugging in xcode, the simulator is still open but goes to the iphone homescreen. When I go to run the app again from xcode, the login session is gone and user is nil, spent a couple hours and not sure why. My question is is stop debugging messing with the session of the logged in user? Does using setAuthorizationHeaderWithUsername have to do with anything.
#property (retain, nonatomic) NSDictionary* user;
Login:
[[API sharedInstance] commandWithParams:params
onCompletion:^(NSDictionary *json) {
//handle the response
NSDictionary* res = [[json objectForKey:#"result"] objectAtIndex:0];
if ([json objectForKey:#"error"]==nil && [[res objectForKey:#"IdUser"] intValue]>0) {
//success
[[API sharedInstance] setUser:res];
}
}];
API:
+(API*)sharedInstance
{
static API *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kAPIHost]];
});
return sharedInstance;
}
You need to serialize your cookies into files or other stores.
See this answer:
I am working with authenticating user to use the google account he is associated with. The problem is that everytime the user logs in through my app, the "Allow Access" always appears on the Google's authentication view even I had clicked the Allow Access already from previous test. Is this normal or am I doing my codes wrong? Please help me guys.
I used the following codes for loggin in an out:
- (IBAction)signIn:(id)sender {
if(!isSignedIn){
[self signOutFromAll];
NSString *keychainItemName = nil;
// save keychain
keychainItemName = kKeychainItemName;
NSString *scope = #"https://www.googleapis.com/auth/plus.me";
NSString *clientID = kClientID;
NSString *clientSecret = kClientSecret;
SEL finishedSel = #selector(viewController:finishedWithAuth:error:);
GTMOAuth2ViewControllerTouch *viewController;
viewController = [GTMOAuth2ViewControllerTouch controllerWithScope:scope
clientID:clientID
clientSecret:clientSecret
keychainItemName:keychainItemName
delegate:self
finishedSelector:finishedSel];
[[self navigationController]pushViewController:viewController animated:YES];
} else {
[self displayAlertWithMessage:#"Currently Signed in."];
} }
- (IBAction)signOut:(id)sender {
[self signOutFromAll];
[self displayAlertWithMessage:#"Signed out."]; }
This is for the delegate:
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error{
if(error != nil){
// Authentication failed...
NSLog(#"Authentication error: %#", error);
NSData *responseData = [[error userInfo] objectForKey:#"data"];
if([responseData length] > 0)
NSLog(#"%#", [[[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding]autorelease]);
self.auth = nil;
} else {
// Authentication succeeded...
isSignedIn = YES;
self.auth = auth;
}
}
And awakeFromNib:
- (void)awakeFromNib{
// Fill in the Client ID and Client Secret text fields
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// First, we'll try to get the saved Google authentication, if any, from the keychain
// Normal applications will hardcode in their client ID and client secret,
// But the sample app allows the user to enter them in a text field, and saves them in the preferences
NSString *clientID = [defaults stringForKey:kGoogleClientIDKey];
NSString *clientSecret = [defaults stringForKey:kGoogleClientSecretKey];
GTMOAuth2Authentication *auth;
auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
clientID:clientID
clientSecret:clientSecret];
if (auth.canAuthorize) {
// There is saved google authentication
// self.serviceSegments.selectedSegmentIndex = 0;
}
// Save the authentication object, which holds the auth tokens
self.auth = auth;
[self setAuth:auth];
isSignedIn = self.auth.canAuthorize;
}
By the way my reference for these codes is on this link: http://code.google.com/p/gtm-oauth2/wiki/Introduction#Using_the_OAuth_2_Controllers
from the docs:
The keychain item name is used to save the token on the user’s keychain, and should identify both your application name and the service name(s). If keychainItemName is nil, the token will not be saved, and the user will have to sign in again the next time the application is run.
http://code.google.com/p/gtm-oauth2/wiki/Introduction
So, from your code, it depends on what kKeychainItemName is set to.
Just thought I'd comment on this as I was reading the docs.
Use this method when you get the oauth object to save into keychain
[GTMOAuth2ViewControllerTouch saveParamsToKeychainForName:YOUR_KEYCHAIN_ITEM_NAME authentication:auth];
and
before making a call to api just check and retrieve the oauth object using this
GTMOAuth2Authentication * auth = [GTMOAuth2ViewControllerTouch
authForGoogleFromKeychainForName:YOUR_KEYCHAIN_ITEM_NAME
clientID:GOOGLE_CLIENT_KEY
clientSecret:GOOGLE_CLIENT_SECRET];
and make sure it's oauth object is authentic with using this
if(![GTMOAuth2ViewControllerTouch authorizeFromKeychainForName:YOUR_KEYCHAIN_ITEM_NAME authentication:auth])
I know this is an old question, but I encountered the same issue so I'm writing my solution, it might help somebody else in the future.
Turns out it's not sufficient to only set self.auth, you also need to set the self.analyticsService.authorizer variable
if ([self.auth canAuthorize])
{
self.analyticsService.authorizer = self.auth;
[self getAnalyticsData];
return;
}
This did the trick for me, the user is no longer asked to enter the credentials.
Put the below code to logout / sign out from Google SDK.
- Call below function from where you want:
static NSString *const kKeychainItemName = #"MY_APP";
- (void)logoutFromGoogleDrive {
[GTMOAuth2SignIn revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)self.driveService.authorizer];
[GTMOAuth2ViewControllerTouch saveParamsToKeychainForName:kKeychainItemName authentication:nil];
}
[Note: Above code works, if you have used GTMOAuth2SignIn for sign in user for google access like,
GTMOAuth2Authentication * auth = [GTMOAuth2ViewControllerTouch
authForGoogleFromKeychainForName:YOUR_KEYCHAIN_ITEM_NAME
clientID:GOOGLE_CLIENT_KEY
clientSecret:GOOGLE_CLIENT_SECRET];
]
From my experience, this behavior is normal.
Are you having doubts because facebook only asks the user once if the user wants to grant the app privileges to access the user's profile?