GTMOAuth2Authentication problems with storing access token - ios

I have trouble using [self isAuthorized] to get confirmation of the access token I've got earlier.
Every time I'm login in with Google Drive SDK for iOS with:
// Creates the auth controller for authorizing access to Google Drive.
-(GTMOAuth2ViewControllerTouch *)createAuthController {
GTMOAuth2ViewControllerTouch *authController;
authController = [[GTMOAuth2ViewControllerTouch alloc] initWithScope:scopes
clientID:kClientID
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
return authController;
}
After the authentification completed, there is no error so the access token should be saved correctly
// Handle completion of the authorization process, and updates the Drive service
// with the new credentials.
-(void)viewController:(GTMOAuth2ViewControllerTouch *)viewController finishedWithAuth:(GTMOAuth2Authentication *)authResult error:(NSError *)error {
if (error != nil)
{
//[self showAlert:#"Authentication Error" message:error.localizedDescription];
self.driveService.authorizer = nil;
}
else
{
self.driveService.authorizer = authResult;
}
}
I used an NSLog to make sure I received the access token and It did.
-(BOOL)isAuthorized {
NSString *oauthToken = [((GTMOAuth2Authentication *)self.driveService.authorizer) accessToken];
NSLog(#"oauthToken: %#", oauthToken);
return [((GTMOAuth2Authentication *)self.driveService.authorizer) canAuthorize];
}
But When I look if I'm authorized or not, there is no token saved (oauthToken is NULL) and I need to login again.
N.B: It was working in the past before iOS 9. I don't know if it is related.
Thanks
Vincent

You might want to try something like this to verify login:
// Check for authorization.
GTMOAuth2Authentication *auth =
[GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
clientID:kClientId
clientSecret:kClientSecret];
if ([auth canAuthorize]) {
[self isAuthorizedWithAuthentication:auth];
}
On the other hand, if you really want the access token, check out this SO post, however, it is not best practice to store the access token, since access token has an expiration time. Good luck & Hope this helps.

Related

Get access token for gmail api via objective-c

I used the code from this sample. To get the number of user's unread messages (that's what I need), I need to send this GET request
https://www.googleapis.com/gmail/v1/users/me/labels/UNREAD?key={MY_API_KEY}
like in this example. But I guess that the {ACCESS_TOKEN} should be here instead of {MY_API_KEY}. If so, could anybody tell me how to get the access token using AFNetworking or auth from the sample?
As discussed in Authorizing Your App with Gmail
Gmail uses the OAuth 2.0 protocol for authenticating a Google account and authorizing access to user data. You can also use Google+ Sign-in to provide a "sign-in with Google" authentication method for your app.
If using AFNetworking is still your preference as requested, you may use the guide on how to get the access token given in this GitHub post - AFOAuth2Manager.
Solution given in this SO post - How to get the number of unread threads in INBOX with Gmail API might also help.
To get the access token to make an authorize request to the Google API you should implement the following methods:
- (GTMOAuth2ViewControllerTouch *)createAuthController {
GTMOAuth2ViewControllerTouch *authController;
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
NSArray *scopes = [NSArray arrayWithObjects:kGTLAuthScopeGmailReadonly, nil];
authController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:[scopes componentsJoinedByString:#" "]
clientID:kClientID
clientSecret:nil
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
return authController;
}
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)authResult
error:(NSError *)error {
if (error != nil) {
...
}
else {
NSLog(#"Access token: %#", authResult.accessToken);
}
}
And your ViewDidAppear method should looks like this:
- (void)viewDidAppear:(BOOL)animated {
if (!self.service.authorizer.canAuthorize) {
// Not yet authorized, request authorization by pushing the login UI onto the UI stack.
[self presentViewController:[self createAuthController] animated:YES completion:nil];
}
That code output the target access token.

User signing in using Amazon Cognito

I am using Amazon Cognito User Pools. I am trying to authenticate a user. First he/she will have to enter the phone number and password, there'll be a SMS sent to authenticate the user, upon Authenticating the user is expected to Sign in by giving the phonenumber and password.
1.) I want to popup the User registration Screen if the user is not registered with the app
2.) If the app has gone to the background I want the user to proceed using the app without having to login again. (At the moment the user requires to sign in all the time when they go to the background)
3.) If the user has registered but not authenticated the SMS validation then I want to redirect the user to the confirmation page
I have been stuck in this for nearly a week now. Can someone help me out.
In the app Delegate I have the following code. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
..
AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:nil];
//create a pool
AWSCognitoIdentityUserPoolConfiguration *configuration = [[AWSCognitoIdentityUserPoolConfiguration alloc] initWithClientId:#"XXX" clientSecret:#"XXX" poolId:#"us-east-1_XXX"];
[AWSCognitoIdentityUserPool registerCognitoIdentityUserPoolWithConfiguration:serviceConfiguration userPoolConfiguration:configuration forKey:#"UserPool"];
//AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:#"UserPool"];
[AWSLogger defaultLogger].logLevel = AWSLogLevelVerbose;
AWSCognitoIdentityUserPool *pool =[AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:#"UserPool"];
pool.delegate = self;
}
//set up password authentication ui to retrieve username and password from the user
-(id<AWSCognitoIdentityPasswordAuthentication>) startPasswordAuthentication {
//
if(!self.navController){
self.navController = [[UIForViewController getStoryboard] instantiateViewControllerWithIdentifier:#"signupSegueID"];
}
// if(!self.signInViewController){
// self.signInViewController = self.navigationController.viewControllers[0];
// }
dispatch_async(dispatch_get_main_queue(), ^{
//rewind to login screen
//display login screen if it isn't already visibile
if(!(self.navController.isViewLoaded && self.navController.view.window))
{
[self.window.rootViewController presentViewController:self.navController animated:YES completion:nil];
}
});
return nil;
}
Please note that startPasswordAuthentication is never executed unless I add the following code in the APPDELEGATES
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
[[self.user getDetails] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserGetDetailsResponse *> * _Nonnull task) {
if (task.error) {
//
NSLog(#"Error ");
[[[UIAlertView alloc] initWithTitle:task.error.userInfo[#"__type"]
message:task.error.userInfo[#"message"]
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil] show];
return nil;
}
AWSCognitoIdentityUserGetDetailsResponse *response = task.result;
for (AWSCognitoIdentityUserAttributeType *attribute in response.userAttributes) {
//print the user attributes
NSLog(#"Attribute: %# Value: %#", attribute.name, attribute.value);
}
return nil;
}];
1) Cognito doesn't currently expose an API to check if a username exists already. You could work around this by calling a username specific API and acting based on the exception thrown back. If you're thinking more locally, you can check the session based on the username to see if someone is already signed in.
2) The RefreshTokens API is used to get a new access token once the old one has expired. Use the refresh token you get back in authenticating to facilitate this.
3) Being registered doesn't give you access. On user registration, you get no token, but are required to log in afterwards. This is already handled.

How do I get refresh token using GIDSignIn and GTMOAuth2Authentication in iOS?

I am writing an iOS app that uses Google's GIDSignIn [1] to sign in users and GTLServiceYoutube to execute queries against Youtube (uploading videos and retrieving Youtube video lists).
This works fine when the user first logs in but after approximately one hour, the access token expires and the user is no longer able to execute queries with GTLServiceYoutube due to a 401 error (invalid credentials).
I use the following code to set the GTMOAuth2Authentication after successful login:
- (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error {
if (error == nil) {
[self setAuthorizerForSignIn:signIn user:user];
}
[super signIn:signIn didSignInForUser:user withError:error];
}
- (void)setAuthorizerForSignIn:(GIDSignIn *)signIn user:(GIDGoogleUser *)user {
GTMOAuth2Authentication *auth = [[GTMOAuth2Authentication alloc] init];
[auth setClientID:signIn.clientID];
[auth setClientSecret:[[NSBundle mainBundle] objectForInfoDictionaryKey:#"GoogleClientSecret"]];
[auth setUserEmail:user.profile.email];
[auth setUserID:user.userID];
[auth setAccessToken:user.authentication.accessToken];
[auth setRefreshToken:user.authentication.refreshToken];
[auth setExpirationDate: user.authentication.accessTokenExpirationDate];
[[UserManager sharedInstance].youTubeService setAuthorizer:auth];
}
where [[UserManager sharedInstance].youTubeService is an instance of GTLServiceYouTube.
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.
So my question is: Am I doing something wrong here? Or am I missing something to get the proper access token in GTMOAuth2Authentication after refresh?
[1] https://developers.google.com/identity/sign-in/ios/api/interface_g_i_d_sign_in
I believe the correct way to do this is by signing the user back in when the app is reopened or the token needs to be refreshed. This can be done by calling [[GIDSignIn sharedInstance] signInSilently] and then, when it finishes signing in update the keychain or datastore with your new auth tokens.
As of GoogleSignIn 2.1.0, making a call to [GIDSignIn sharedInstance].signInSilently; updates the credentials stored in [GIDSignIn sharedInstance].currentUser.authentication.
Run pod update on your project to update to the 2.1.0 SDK if you're using Cocoapods.
With GTMOAuth2Authentication you can 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);
}
}];

iOS: Google Authentication Code

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?

Using Flattr API v2 with iOS

I'm currently building an iOS application and want to include Flattr-Support over the Flattr-API v2.
I've already created my application at https://flattr.com/apps/ and got the key and secret.
The problem is that I have to provide a callback-URL in the application-settings at flattr even if I select "client" as application type. In addition only http://... callback-URLs seem to be allowed in the input field so I can't set a callback URL to open my application (something like myApp://...)
How do I implement the Flattr oAuth process for client applications?
Are there any detailed instructions how to implement the flattr-authentication with a non-web-based / iOS application?
I planned to use the JDG OAuthConsumer library but this doesn't seem to work - any other iOS librarys I could use?
A short description of my implementation using the Flattr API v2 to flattr a thing from my iOS application:
I'm currently using the "Google Toolbox for Mac - OAuth 2 Controllers":
http://code.google.com/p/gtm-oauth2/
Create a Token to be authenticated:
- (GTMOAuth2Authentication *)flattrAuth {
NSURL *tokenURL = [NSURL URLWithString:#"https://flattr.com/oauth/token"];
// We'll make up an arbitrary redirectURI. The controller will watch for
// the server to redirect the web view to this URI, but this URI will not be
// loaded, so it need not be for any actual web page.
NSString *redirectURI = #"http://localhost/"; //for me localhost with / didn't work
GTMOAuth2Authentication *auth;
auth = [GTMOAuth2Authentication authenticationWithServiceProvider:#"MyApplication"
tokenURL:tokenURL
redirectURI:redirectURI
clientID:clientKey
clientSecret:clientSecret];
return auth;
}
Create a ViewController to authenticate the token:
- (GTMOAuth2ViewControllerTouch*)getSignInViewController{
GTMOAuth2Authentication *auth = [self flattrAuth];
// Specify the appropriate scope string, if any, according to the service's API documentation
auth.scope = #"flattr";
NSURL *authURL = [NSURL URLWithString:#"https://flattr.com/oauth/authorize"];
GTMOAuth2ViewControllerTouch *viewController;
viewController = [[[GTMOAuth2ViewControllerTouch alloc] initWithAuthentication:auth
authorizationURL:authURL
keychainItemName:keychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)] autorelease];
return viewController;
}
and the delegate method:
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
if (error != nil) {
DLog(#"Flattr sign-in failed with error: %#", [error localizedDescription]);
} else {
DLog(#"Flattr Signin success");
authToken = [auth retain];
}
}
You can display the Viewcontroller in your application - it displays the flattr-login to the user so he can authenticate the application.
You can flattr a thing with the authentication token this way:
NSString* flattrURL = #"https://api.flattr.com/rest/v2/things/%qi/flattr";
NSURL* u = [NSURL URLWithString:[NSString stringWithFormat:flattrURL, item.flattrThingID]];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:u];
[authToken authorizeRequest:request completionHandler:^(NSError *error){
if (error == nil) {
// the request has been authorized
NSURLConnection* connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
if(!connection){
//TODO: handle error
} else {
[connection start];
}
} else {
//TODO: handle error
}
}];
Now implement the NSURLConnectection delegate methods and parse the JSON responses.
The GTMOAuth2 library allows you to save the authenticated token to the keychain. Look at their introduction at http://code.google.com/p/gtm-oauth2/wiki/Introduction#Retrieving_Authorization_from_the_Keychain for instructions.
When you wan't to authenticate a desktop/mobile app you would wan't to use the oauth2 implicit grant flow. As you register your flattr application use a application specific URI that will callback to your application, ex. iphone-application://oauth-callback.
When you authenticate the application with us you use the response_type token instead of code. This will create a token at once and redirect you back to your application.
Ex. request URL: https://flattr.com/oauth/authorize?client_id=2134&redirect_uri=iphone-application://oauth-callback&response_type=token
If the resource owner will authorize your application we will send a HTTP 302 and redirect you back to your redirect uri.
Ex. response 302 Location: iphone-application://oauth-callback#access_token=e5oNJ4917WAaJaO4zvoVV2dt3GYClPzp&token_type=bearer
Currently we don't have any detailed documentation explaining how to do the implicit grant but we are working on the documentation. Meanwhile i'm all ears.
https://github.com/nxtbgthng/OAuth2Client is a iOS oauth2 library but I don't know if it's any good.
This one looks good: https://github.com/neonichu/FlattrKit

Resources