Question

I am using Google's GTMOAuth for iOS/Mac SDK in order to be able to connect with APIS such as YouTube API and Instagram API. It all works fine, however when the user authenticates, I can only get access token. This is all well and good, but after a while the access tokens expire and the user has to re-login which is awful.

My problem is that when the user authenticates, I ONLY GET back an access token and nothing else...

Thanks for you're help :)

Was it helpful?

Solution

May be this one help you..!

I am using Google oauth 2.0, for google drive authentication.

In the Google finished with authentication method save the accesstoken and refresh token values in NSUSerDefaults like this.

 - (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;

            self.auth = authResult;

            NSLog(@"\n accessToken value is %@",self.auth.accessToken);

            [[NSUserDefaults standardUserDefaults] setValue:self.auth.refreshToken forKey:@"refreshToken"];

            [[NSUserDefaults standardUserDefaults] setValue:self.auth.accessToken forKey:@"accessToken"];

            [[NSUserDefaults standardUserDefaults] synchronize];


        }

    }

After that when ever you want to use the acces token for performing api call first make a call using existing accesstoken value from the NSUSERDefaults, after that in the url response check the status code. If you are getting status code '401' means your access token is expired, and not valid. Then you have to request for refresh token using saved refresh token value in NSUserDefaults like this.

This is your first api call to check accesstoken valid or not.

    - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse *)response
    {
        NSLog(@"Did Receive Response %@", response);
        NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
        //int responseStatusCode = [httpResponse statusCode];
        if (httpResponse.statusCode == 200) {
//Your api call success and accesstoken is valid.


        } else if(httpResponse.statusCode == 401 && connection == self.conn2) {

            [[self appDelegate] refreshAccessToken];
        } 
    }

This is for request new accesstoken from refresh token.

-(void)refreshAccessToken {


    NSString *requestString = [NSString stringWithFormat:@"https://accounts.google.com/o/oauth2/token"];
        NSString *string = [NSString stringWithFormat:@"client_id=%@&client_secret=%@&refresh_token=%@&grant_type=refresh_token",kClientID,kClientSecret,[[NSUserDefaults standardUserDefaults] valueForKey:@"refreshToken"]];
    NSData *postData = [string dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:requestString]];
    NSLog(@"\n request str : %@",request);

    NSLog(@"\n refresh token value is %@",[[NSUserDefaults standardUserDefaults] valueForKey:@"refreshToken"]);
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:postData];
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];//connectionWithRequest:request delegate:self];
    if(connection)
    {
        NSLog(@"Connection Successful");
    }
    else
    {
        NSLog(@"Connection could not be made");
    }

    [connection start];


}

In response check for status code again and if status code is 200.Then update the value in userdafaults.

- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"Did Receive Response %@", response);
    //NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
    self.data = [NSMutableData data];
}

- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
    NSLog(@"Did Receive Data %@", data);
    [self.data appendData:data];
}

- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
{
    NSLog(@"Did Fail %@",error);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"Did Finish");
    // Do something with responseData
    NSError *err;
    id JSon = [NSJSONSerialization JSONObjectWithData:self.data options:kNilOptions error:&err];
    if (err) {
        NSLog(@"%@",err);
    }
    else {
        NSLog(@"Json %@",JSon);
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"accessToken"];
        [[NSUserDefaults standardUserDefaults] setObject:[JSon valueForKey:@"access_token"] forKey:@"accessToken"];
        [[NSUserDefaults standardUserDefaults] synchronize];

    }
}

This is my first answer on stack overflow. Sorry for any mistakes.

UPDATE BELOW - Written by Supertecnoboff

Also keep this in mind. For some API's such as Google, you need to add "approval_prompt=force" and "access_type=offline" if you want it to give you a refresh token. In order to add these parameters, you have to edit the GTMOAuth2SignIn.m file and replace the "paramsDict" with this NSMutableDictionary:

NSMutableDictionary *paramsDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                 @"code", @"response_type",
                                 clientID, @"client_id",
                                 scope, @"scope", // scope may be nil
                                 @"force", @"approval_prompt",
                                 @"offline", @"access_type",
                                 nil];

OTHER TIPS

You can also use the GTMOAuth2Authentication´s authorizeRequest:completionHandler: method

If your application saves the authorization to the keychain (by setting the controller's keychainItemName), it can be retrieved the next time the application launches:

GTMOAuth2Authentication *auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
                                                                                          clientID:kClientID
                                                                                    clientSecret:kClientSecret];

NSLog(@"accessToken: %@", auth.accessToken); //If the token is expired, this will be nil

and then you can refresh the access token like this:

// authorizeRequest will refresh the token, even though the NSURLRequest passed is nil
[auth authorizeRequest:nil
             completionHandler:^(NSError *error) {
                 if (error) {
                     NSLog(@"error: %@", error);
                 }
                 else {
                     NSLog(@"accessToken: %@", auth.accessToken); //it shouldn´t be nil
                 }
             }];

The token will be then refreshed and you can keep making queries.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top