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.
Related
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.
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.
UPDATE 2015-10-28 - Release 2.4.0 of Google Sign-In for iOS appears to have resolved this problem, with both the idToken and the accessToken being refreshed as necessary by the new GIDAuthentication methods getTokensWithHandler: and refreshTokensWithHandler:. GIDSignIn method SignInSilently also refreshes both tokens.
I'm using AWS Mobile SDK for iOS, and I've implemented Google sign-in as a Cognito credentials provider using the AWS Cognito Sync sample code as a basis. The sign-in (and subsequent silent sign-in) flow is working correctly and signed-in users can access AWS resources like DynamoDB as intended.
My problem is that the user.authentication.idToken expires after one hour and at that point calls to AWS services fail with authentication errors. I can refresh the user.authentication.accessToken using
[self.googleUser.authentication refreshAccessTokenWithHandler:^(NSString *accessToken, NSError *error) {...}
but this doesn't update the idToken. I've also tried calling
[[GoogleSignIn sharedInstance] signInSilently];
which gives me a valid idToken the first time it is called in the session, but although it completes successfully it doesn't refresh the idToken on subsequent calls within the same session.
I've been checking/dumping the token contents using
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=<idToken>
and
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=<accessToken>
I'm not sure whether the problem is that GIDSignIn is not updating idToken, or that AWS should be using the refreshToken somehow to auto-refresh the user at the backend. Either way I'm out of ideas.
Code snippets below. I've tested using GoogleSignIn 2.2.0 and 2.3.2 and experienced the same problem.
...
#interface MySignInClass <GIDSignInDelegate>
...
-(void) signInWithGoogle
{
GIDSignIn *signIn = [GIDSignIn sharedInstance];
signIn.clientID = MY_GOOGLE_CLIENT_ID;
signIn.shouldFetchBasicProfile = YES;
signIn.scopes = [NSArray arrayWithObjects:#"https://www.googleapis.com/auth/userinfo.profile", #"openid", nil];
signIn.delegate = self;
if([signIn hasAuthInKeychain]) {
[signIn signInSilently];
} else {
[signIn signIn];
}
}
...
- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)error
{
if (error != nil) {
[self handleSignInError:error]; // Handle error
}
else {
NSString *idToken = user.authentication.idToken;
NSDictionary* logins = #{#"accounts.google.com": idToken};
self.credentialsProvider = [[AWSCognitoCredentialsProvider alloc]
initWithRegionType:MY_COGNITO_REGION_TYPE
identityId:nil
identityPoolId:MY_COGNITO_IDENTITY_POOL
logins:logins];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc]
initWithRegion:MY_COGNITO_REGION
credentialsProvider:self.credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
// AWSTask stuff removed for simplicity
AWSTask* task = [self.credentialsProvider getIdentityId];
...
}
}
...
- (void)signIn:(GIDSignIn *)signIn
didDisconnectWithUser:(GIDGoogleUser *)user
withError:(NSError *)error
{
[self handleGoogleSignout]; // Do signout stuff
}
...
Call the GIDSignIn's signInSilently method like so:
[[GIDSignIn sharedInstance] signInSilently];
This will end up calling your signIn:didSignInForUser:withError: delegate method implementation with the idToken again.
I have verified that with the 2.4.0 release of Google Sign-In for iOS the above approach of using signInSilently does indeed work for giving you a new idToken that isn't expired.
I had this problem too. Tried everything possible, that token is simply not being renewed by G+ iOS SDK.
The solution on our side was to send refresh_token to our server, and they were able to correctly renew the token on their side, by accessing one Google API, and then sending the new token back to my app. Unfortunately I don't have access now there, to tell you exactly which API, only tomorrow.
How about using a timer at 30 mins., then calling the signInSilently?
SHORT STORY
Using gtm-oauth2 for iOS and FOSOAuthServerBundle in Symfony2 to implement my own Oauth2 server I am not getting the callBack finishedSelector to be invoked.
This is where the "special" ViewController is created:
GTMOAuth2ViewControllerTouch * viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithAuthentication:myAuth
authorizationURL:authURL
keychainItemName:nil
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
What are the reasons that might make finishedSelector, (the implemented method viewController:finishedWithAuth:error) not to be invoked?
The behavior I get is that the login page is properly rendered, but it acts as the starting point of the whole web application, rendering the rest of the pages once it is logged-in instead of returning the control to the finishedSelector and, finally, to the view controller that has to manage the continuation of the APP workflow.
LONG STORY
Using gtm-oauth2 and FOSOAuthServerBundle in Symfony2, I am experiencing problems trying to make the arquitecture to catch the login and load the authenticated session from my iOS APP.
I am following the instructions described in the gtm-oauth2 documentation, particularly the Signing in to non-Google Services part.
Doing what it is described there, I have this method for creating the auth object:
- (GTMOAuth2Authentication * ) authForMyAPP
{
//This URL is defined by the individual 3rd party APIs, be sure to read their documentation
NSString * url_string = #"http://myHost/oauth/v2/token";
NSURL * tokenURL = [NSURL URLWithString:url_string];
// 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. This needs to match the URI set as the
// redirect URI when configuring the app.
NSString * redirectURI = #"http://myHost/oauth/v2/falseCallBack";
GTMOAuth2Authentication * myAuth;
myAuth = [GTMOAuth2Authentication authenticationWithServiceProvider:#"MyAPP"
tokenURL:tokenURL
redirectURI:redirectURI
clientID:kMyClientID
clientSecret:kMyClientSecret
];
//[myAuth setTokenType:#"Bearer"];
return myAuth;
}
And then, this method creates the "special" viewController that should handle the render of the login page and returning the control when the login is performed:
- (void)signInToMyAPP()
{
GTMOAuth2Authentication *myAuth = [self authForMyAPP];
NSString* auth_string = #"http://127.0.0.1/~pgbonino/Symfony/web/app.php/oauth/v2/auth";
NSURL * authURL = [NSURL URLWithString:auth_string];
// Display the authentication view
// Creates the "special" viewController passing the `auth` object, the authorization URL and the finishedSelector
GTMOAuth2ViewControllerTouch * viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithAuthentication:myAuth
authorizationURL:authURL
keychainItemName:nil
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[self.navigationController pushViewController:viewController animated:YES];
}
Finally, I have the method used for that finishedSelector. It should be called once the login is properly performed and the authentication has succeeded (or an error has come). THAT IS WHAT I AM NOT GET DONE:
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)myAuth
error:(NSError *)error
{
if (error != nil)
{
// Authentication failed
UIAlertView *alertView = [ [UIAlertView alloc] initWithTitle:#"Authorization Failed"
message:[error localizedDescription]
delegate:self
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
else
{
// Authentication succeeded
// Assign the access token to the instance property for later use
//self.accessToken = myAuth.accessToken;
[myAuth setShouldAuthorizeAllRequests:YES];
[[Singleton sharedSingleton] setAuth:myAuth];
// Display the access token to the user
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Authorization Succeeded"
message:[NSString stringWithFormat:#"Access Token: %#", myAuth.accessToken]
delegate:self
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
}
This all is supposed to render my login page in a web view and catch the successful login to call the viewController:finishedWithAuth:error and save the session in some shared object.
Nevertheless, the behavior I am getting is that I get rendered the login in the web view, I correctly login and, instead oF the delegated selector gets invoked, it just normally logs in the application and the next page is loaded in the web view, as if it was in a normal browser. So the callback is not performed.
Why am I not getting the selector to be called? Any idea?
IMPORTANT NOTE: the Oauth2 server works perfectly: if I call the token URL and the callBack url from Safari, everything works well. Tokens and auths codes are correctly saved in database.
Forget it.
It was just me.
OAuth2 won't work with Symfony2 and FOSUserBundle while this parameter is set to true in config.yml:
always_use_default_target_path: false
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