gtm-oauth2 access to own server configuration - ios

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

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.

GTM OAuth 2.0 on iOS - retrieving user's email

GTMOAuth 2.0 seems like an excellent tool for OAuth 2.0 verification on iOS. I am trying to retrieve the full name and email of a Google user by implementing GTMOAuth-2 in Xcode but am having a bit of trouble. Based on this answer: Retrieve User email using GTM OAuth2 for iOS, it should be as easy as calling auth.userEmail. However, the problem is that calling auth.userEmail in the following code segment always return null:
- (void)viewController:(GTMOAuth2ViewControllerTouch * )viewController
finishedWithAuth:(GTMOAuth2Authentication * )auth
error:(NSError * )error
{
NSLog(#"finished");
NSLog(#"auth access token: %#", auth.accessToken);
[self.navigationController popToViewController:self animated:NO];
if (error != nil) {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Error Authorizing with Google"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
} else {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Success Authorizing with Google"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
NSLog(#"email: %#",auth.userEmail);
}
The code runs successfully and retrieves an access token, but auth.userEmail is always null. Do I need to make a request to the Google email endpoint using GTMOAuth 2.0's Fetcher object, or otherwise send an additional HTTP GET request to retrieve the user's email using auth.accessToken?
I recently worked on Google OAuth2 for logging user in with gmail
by following tutsplus's tutorial and It gave me the desired results.I
would recommend you to follow this link. This provides methods to
login and logout and also email address of logged in user. Google
OAuth2
. And to get email address of logged in user, add this in scopes
https://www.googleapis.com/auth/userinfo.email. and code will look
like this
[_googleOAuth authorizeUserWithClienID:#"YOUR CLIENT ID"
andClientSecret:#"SECRET"
andParentView:self.view
andScopes:[NSArray arrayWithObjects:#"https://www.googleapis.com/auth/userinfo.profile",#"https://www.googleapis.com/auth/userinfo.email", nil]];
And for GTM OAuth 2.0, add this
scopehttps://www.googleapis.com/auth/userinfo.email .Hope this helps
you.

iOS: HTTP Basic/Digest Auth with a UIWebView

Overview
I'm working on a SAML login (single sign-on, similar to openID) solution for an iOS app that involves showing a view controller with a UIWebView and I'm running into a timing and/or timeout issue when handling HTTP basic/digest auth in the UIWebView.
Specifically, when the client gets an HTTP auth challenge, I pop an UIAlertView prompting the user for a userID & password. If the user is able to enter the info quickly (< 10 seconds), it works. However, if the entry takes more than 10 seconds, the connection appears to have been terminated and nothing happens.
Questions
Is there a timeout on calls to connection:didReceiveAuthenticationChallenge: that would prevent me from prompting the user for a userID & password (and having to wait for user input)? Does anyone have a workaround (e.g. some way to extend the connection timeout)?
Is there a better way to handle HTTP basic/digest auth from a UIWebView than a subclass of NSURLProtocol?
Details & Code
For most of the SAML systems we need to handle, the login will appear as a regular web page in the UIWebView. However, some of the systems we need to handle fall back to using HTTP basic or HTTP digest authentication for mobile browsers, so we need to be able to handle that as well.
The big challenges start with the fact that UIWebView does not expose the network calls underneath. To get at what I need, I've created a subclass of NSURLProtocol and registered it, as necessary:
[NSURLProtocol registerClass:[SMURLProtocol class]];
With that, this method on SMURLProtocol gets called when an HTTP basic/auth challenge is issued, so I return YES we can handle HTTP basic & digest authentication:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]
|| [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]);
}
Now I've told the networking stack that SMURLProtocol can handle the auth challenge, so it calls
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
NSString *authenticationMethod = [protectionSpace authenticationMethod];
if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]
|| [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]) {
// Stash the challenge in an IVAR so we can use it later
_challenge = challenge;
// These network operations are often on a background thread, so we have to make sure to be on the foreground thread
// to interact with the UI. We tried the UIAlertView performSelectorOnMainThread, but ran into issues, so then
// we switched to GCD with a semaphore?
_dsema = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_main_queue(), ^{
// Prompt the user to enter the userID and password
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"AUTHENTICATION_REQUIRED", #"")
message:[protectionSpace host]
delegate:self
cancelButtonTitle:NSLocalizedString(#"CANCEL", #"")
otherButtonTitles:NSLocalizedString(#"LOG_IN", #""), nil];
[alert setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
[alert show];
});
dispatch_semaphore_wait(_dsema, DISPATCH_TIME_FOREVER);
// --> when you get here, the user has responded to the UIAlertView <--
dispatch_release(_dsema);
}
}
As you can see, I'm launching an UIAlertView to prompt the user for a userID and password. I have to do that back on the main thread because (apparently, I don't know for certain) the networking code is running on a background thread. I added the semaphore and explicit Grand Central Dispatch code to work around occasional crashes I was seeing (based upon this thread).
The final piece is the UIAlertView delegate that accepts the userID & password builds the credential for the challenge:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (([alertView alertViewStyle] == UIAlertViewStyleLoginAndPasswordInput) && (buttonIndex == 1)) {
NSString *userID = [[alertView textFieldAtIndex:0] text];
NSString *password = [[alertView textFieldAtIndex:1] text];
// when you get the reply that should unblock the background thread, unblock the other thread:
dispatch_semaphore_signal(_dsema);
// Use the userID and password entered by the user to proceed
// with the authentication challenge.
[_challenge.sender useCredential:[NSURLCredential credentialWithUser:userID
password:password
persistence:NSURLCredentialPersistenceNone]
forAuthenticationChallenge:_challenge];
[_challenge.sender continueWithoutCredentialForAuthenticationChallenge:_challenge];
_challenge = nil;
}
}
As I said in the overview, this all works great if the user is able to input the userID & password in less than about 10 seconds. If it takes longer, the connection appears to get timed out and passing the credentials on to the challenge's sender has no effect.

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