2

I have a server with OAuth 2.0 implemented for issuing access and refresh tokens. The client for this server is an iOS App written in Objective-C. I am currently using AFNetworking 3.0 for HTTP requests and AFOAuth2Manager to handle authorization. I want to refresh my access token stored in iOS app using the refresh token issued by the server before the access token expires (server returns number of seconds to expire as { 'expires_in': 3600 } (one hour)). Everything is working fine until the access token expires. Below is my code for handling requests and authorization.

- (AFJSONRequestSerializer *)setRequestSerializer
{
    AFJSONRequestSerializer *serializer = [AFJSONRequestSerializer serializer];

    [serializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [serializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];

    User *currentUser = [User currentUser];
    if (currentUser){
        AFOAuthCredential *credentials = [AFOAuthCredential retrieveCredentialWithIdentifier:kEndpointServer];
        if (!credentials.isExpired){
            [serializer setAuthorizationHeaderFieldWithCredential:credentials];
        }
    }

    return serializer;
}

- (AFJSONResponseSerializer *)setResponseSerializer
{
    AFJSONResponseSerializer *serializer = [AFJSONResponseSerializer serializer];

    return serializer;
}

- (AFSecurityPolicy *)setSecurityPolicy
{
    NSString *certFilePath = [[NSBundle mainBundle] pathForResource:@"cert" ofType:@"cer"];
    NSData *certData = [NSData dataWithContentsOfFile:certFilePath];
    NSSet *pinnedCerts = [NSSet setWithObject:certData];

    AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:pinnedCerts];
    [policy setAllowInvalidCertificates:YES]; // DEVELOPMENT ONLY
    [policy setValidatesDomainName:NO];

    return policy;
}

- (AFHTTPSessionManager *)sessionManager
{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    manager.securityPolicy = [self setSecurityPolicy];
    manager.requestSerializer = [self setRequestSerializer];
    manager.responseSerializer = [self setResponseSerializer];

    return manager;
}

- (AFOAuth2Manager *)OAuth2Manager
{
    NSURL *baseURL = [NSURL URLWithString:kEndpointServer];
    AFOAuth2Manager *manager = [[AFOAuth2Manager alloc] initWithBaseURL:baseURL clientID:kParamAPIClientId secret:kParamAPIClientSecret];
    manager.securityPolicy = [self setSecurityPolicy];
    return manager;
}

- (void)loginUser:(NSDictionary *)user block:(void (^)(BOOL, NSError *))result
{
    // Set endpoint URL
    NSString *loginEndpointURL = [NSString stringWithFormat:@"%@%@", kEndpointServer, kEndpointLogin];

    AFHTTPSessionManager *manager = [self sessionManager];

    if ([self internetConnectionAvailable]){
        [manager POST:loginEndpointURL parameters:user progress:nil success:^(NSURLSessionDataTask *task, id responseObject){

            NSDictionary *responseDict = (NSDictionary *)responseObject;
            BOOL success = (BOOL)[(NSNumber *)[responseDict objectForKey:kParamSuccess] boolValue];
            NSString *msg = (NSString *)[responseDict objectForKey:kParamMessage];

            if (success){

                // Get user
                NSDictionary *userLoggedIn = (NSDictionary *)[responseDict objectForKey:kParamUser];

                //NSLog(@"Logged in.");

                NSString *tokenEndpointURL = [NSString stringWithFormat:@"/api%@%@", kEndpointOAuth, kEndpointToken];
                OAuth2Manager *OAuth2Manager = [self OAuth2Manager];
                [OAuth2Manager authenticateUsingOAuthWithURLString:tokenEndpointURL username:(NSString *)[user objectForKey:kParamEmail] password:(NSString *)[user objectForKey:kParamPassword] scope:nil success:^(AFOAuthCredential *credentials){
                    NSLog(@"Credentials:");
                    NSLog(@"Access Token: %@", credentials.accessToken);
                    NSLog(@"Refresh Token: %@", credentials.refreshToken);

                    // Store credentials
                    [AFOAuthCredential storeCredential:credentials withIdentifier:kEndpointServer];

                    // Set current user
                    [User setCurrentUser:userLoggedIn];

                    result(YES, nil);
                }failure:^(NSError *error){
                    NSLog(@"Error authenticating user: %@", error);
                    result(NO, error);
                }];
            } else {
                result(NO, [NSError errorWithDomain:msg code:kEDHTTPRequestFailedErrorCode userInfo:nil]);
            }
        }failure:^(NSURLSessionDataTask *task, NSError *error){
            result(NO, error);
        }];
    } else {
        result(NO, [NSError errorWithDomain:kEDNoInternetConnectionErrorDomain code:kEDNoInternetConnectionErrorCode userInfo:nil]);
    }
}

I have found a similar question on SO:

How to automatically refresh expired token with AFOAuth2Manager?

But the problem with the answer given is that it is outdated (Works with AFNetworking 2.X.X, but does not work with AFNetworking 3.0).

What is the best practice for handling the refreshing of the access token automatically?

Community
  • 1
  • 1
flizana
  • 569
  • 6
  • 26

0 Answers0