Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FitBit: Authorization header required #200

Open
chrisfoulds opened this issue Jan 10, 2016 · 8 comments
Open

FitBit: Authorization header required #200

chrisfoulds opened this issue Jan 10, 2016 · 8 comments

Comments

@chrisfoulds
Copy link

I am trying to connect to Fitbit in an iOS App
https://dev.fitbit.com/docs/oauth2/#authorization-page

I have put this code in the app startup

    [[NXOAuth2AccountStore sharedStore] setClientID:@"xxxxx"
                                             secret:@"yyyyyyyy"
                                            scope:[NSSet setWithObjects:@"activity", @"heartrate", @"weight",nil]
                                   authorizationURL:[NSURL URLWithString:@"https://www.fitbit.com/oauth2/authorize"]
                                           tokenURL:[NSURL URLWithString:@"https://api.fitbit.com/oauth2/token"]
                                        redirectURL:[NSURL URLWithString:@"myapp://handleOauthLogin"]
                                      keyChainGroup:@""
                                          tokenType:@"Bearer"
                                     forAccountType:@"fitbit"];

    NSMutableDictionary *configuration = [NSMutableDictionary dictionaryWithDictionary:[[NXOAuth2AccountStore sharedStore] configurationForAccountType:@"fitbit"]];
    NSDictionary *customHeaderFields = [NSDictionary dictionaryWithObject:@"application/x-www-form-urlencoded" forKey:@"Content-Type"];
    [configuration setObject:customHeaderFields forKey:kNXOAuth2AccountStoreConfigurationCustomHeaderFields];
    [[NXOAuth2AccountStore sharedStore] setConfiguration:configuration forAccountType:@"fitbit"];

In my callback I do

[[NXOAuth2AccountStore sharedStore] handleRedirectURL: url];

and I invoke the auth using :

[[NXOAuth2AccountStore sharedStore] requestAccessToAccountWithType:@"fitbit"];

and I have the notification handlers that return as failure.

The app launches, safari, I login , say everything is ok, it then asks to return to my app.
I say ok, it returns but the notification from storing the token says

oauthConnection Error: {"errors":[{"errorType":"invalid_request","message":"Authorization header required. Visit https://dev.fitbit.com/docs/oauth2 for more information on the Fitbit Web API authorization process."}],"success":false}

I just can not see what I have missed, any help appreciated.

@chrisfoulds
Copy link
Author

So managed to get it working with a hack

In NXOauth2Connection.m / - (NSURLConnection *)createConnection;

I added in :

    NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", client.clientId, client.clientSecret];
    [startRequest setValue:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)] forHTTPHeaderField:@"Authorization"];

@jwlondon98
Copy link

@chrisfoulds hey i had the same problem as you although your solution did not help me. do you think you could send your code

@chrisfoulds
Copy link
Author

That is it ! - all the code I had to add was those two lines.
I can't give the entire source file as it's under copyright

@jwlondon98
Copy link

@chrisfoulds oh, i see.. I'm extremely new to github and oauth so any help would be very useful. I believe the error that I am still getting (the same one you got) has to do with the NXOAuth2Connect.m file. I may not have placed the code you used to hack it in the correct spot but its in the createConnetion method. All I can think of is that there is a bit of code in that method that has been deprecated to NSURLSession. I believe everything else I have is correct I just get the error 'Authorization header required' whenever I make a GET request.

//This is the code in my AppDelegate.m file
//OAuth config
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[NXOAuth2AccountStore sharedStore] setClientID:@"XxXxXx"
                                             secret:@"XxXxXxXxXx"
                                              scope:[NSSet setWithObjects:@"activity", @"heartrate", @"location", @"nutrition", @"profile", @"settings", @"sleep", @"social", @"weight", nil]
                                   authorizationURL:[NSURL URLWithString:@"https://www.fitbit.com/oauth2/authorize?response_type=token&client_id=227NJP&redirect_uri=myapp://handleOauthLogin&scope=activity%20heartrate%20location%20nutrition%20nutrition%20profile%20settings%20sleep%20social%20weight&expires_in=2592000"]
                                           tokenURL:[NSURL URLWithString:@"https://api.fitbit.com/oauth2/access_token"]
                                        redirectURL:[NSURL URLWithString:@"myapp://handleOauthLogin"]
                                      keyChainGroup:@""
                                          tokenType:@"Bearer"
                                     forAccountType:@"fitbit"];

    NSMutableDictionary *configuration = [NSMutableDictionary dictionaryWithDictionary:[[NXOAuth2AccountStore sharedStore] configurationForAccountType:@"fitbit"]];

    NSDictionary *customHeaderFields = [NSDictionary dictionaryWithObject:@"application/x-www-form-urlencoded" forKey:@"Content-Type"];

    [configuration setObject:customHeaderFields forKey:kNXOAuth2AccountStoreConfigurationCustomHeaderFields];

    [[NXOAuth2AccountStore sharedStore] setConfiguration:configuration forAccountType:@"fitbit"];

    [[NXOAuth2AccountStore sharedStore] requestAccessToAccountWithType:@"fitbit"];

    return YES;
}

//Callback
- (BOOL) application:(UIApplication *)app openURL:(NSURL *)url options: (NSDictionary<NSString *, id> *)options
{
    NSLog(@"We received a callback");
    return [[NXOAuth2AccountStore sharedStore] handleRedirectURL:url];
}
//This is the code in my ViewController.m file
//I have two buttons - one handling login, the other using a GET to access a profile
- (IBAction)fitbitBtn:(id)sender
{
    [[NXOAuth2AccountStore sharedStore]
     requestAccessToAccountWithType:@"Fitbit"];
    NSLog(@"LOGIN");
}

- (IBAction)getData:(id)sender
{
    NSLog(@"GET DATA");

    NXOAuth2Account *account = [[NXOAuth2AccountStore sharedStore] accountWithIdentifier:@"fitbit"];

    [NXOAuth2Request performMethod:@"GET"
                        onResource:[NSURL URLWithString:@"https://api.fitbit.com/1/user/-/profile.json"]
                   usingParameters:nil
                       withAccount: account
               sendProgressHandler:nil
                   responseHandler:
     ^(NSURLResponse *response, NSData *responseData, NSError *error){
         NSLog(@"dataAsString %@", [NSString stringWithUTF8String:[responseData bytes]]);
     }];
}
//Here is the code in my NXOAuth2Connect.m file under -(NSURLConnection *)createConnection
- (NSURLConnection *)createConnection;
{
    // if the request is a token refresh request don't sign it and don't check for the expiration of the token (we know that already)
    NSString *oauthAuthorizationHeader = nil;
    if (client.accessToken &&
        ![[requestParameters objectForKey:@"grant_type"] isEqualToString:@"refresh_token"]) {

        // if token is expired don't bother starting this connection.
        NSDate *tenSecondsAgo = [NSDate dateWithTimeIntervalSinceNow:(-10)];
        NSDate *tokenExpiresAt = client.accessToken.expiresAt;
        if (client.accessToken.refreshToken && [tenSecondsAgo earlierDate:tokenExpiresAt] == tokenExpiresAt) {
            [self cancel];
            [client refreshAccessTokenAndRetryConnection:self];
            return nil;
        }

        NSString *tokenType = client.accessToken.tokenType;
        if (tokenType == nil) {
            tokenType = client.tokenType;
        }
        if (tokenType == nil) {
            tokenType = @"OAuth";
        }

        oauthAuthorizationHeader = [NSString stringWithFormat:@"%@ %@", tokenType, client.accessToken.accessToken];
    }

    NSMutableURLRequest *startRequest = [request mutableCopy];
    [self applyParameters:requestParameters onRequest:startRequest];

    if (oauthAuthorizationHeader) {
        [startRequest setValue:oauthAuthorizationHeader forHTTPHeaderField:@"Authorization"];
    }

    if (client.userAgent && ![startRequest valueForHTTPHeaderField:@"User-Agent"]) {
        [startRequest setValue:client.userAgent forHTTPHeaderField:@"User-Agent"];
    }

    if (client.acceptType) {
        [startRequest setValue:client.acceptType forHTTPHeaderField:@"Accept"];
    }

    //This next line gives the following warning - 'initWithRequest:delegate:startImmediately:' is deprecated: first deprecated in iOS 9.0 - Use NSURLSession (see NSURLSession.h)
    //Not sure if that is part of the problem
    NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:startRequest delegate:self startImmediately:NO];
    [aConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [aConnection start];

    if (!sendConnectionDidEndNotification) [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2ConnectionDidStartNotification object:self];
    sendConnectionDidEndNotification = YES;

    //Here is the code you previously provided.. not sure if i placed it in the correct area
    NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", client.clientId, client.clientSecret];
    [startRequest setValue:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)] forHTTPHeaderField:@"Authorization"];

    NSLog(@"%@", aConnection);

    return aConnection;
}

@jwlondon98
Copy link

@chrisfoulds also, not sure if this helps but if I move the two lines you provided in the 'NXOAuth2Connection.m' file up right before the NSURLConnection *aConnection bit I get the following error..

{"errors":[{"errorType":"invalid_client","message":"Invalid authorization header. Client id invalid. Visit https://dev.fitbit.com/docs/oauth2 for more information on the Fitbit Web API authorization process."}],"success":false}oper/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Accounts.framework/Accounts

Like I said I'm not sure if I have your code in the correct location but I suppose that error shows that its a problem with the encoding for the clientID. I honestly have no clue what to do :(

@chrisfoulds
Copy link
Author

Your error is different to mine... however
So I just took a look at the code and my notes, totally forget I dropped this Oauth2 client as I could never get it to work.
I used https://github.com/AFNetworking/AFOAuth2Manager and was up and running in less than a day.
You do have to add the Bearer credential on each request (one line of code) but that is it.

@jwlondon98
Copy link

@chrisfoulds ok thank you very much I will look into using AFNetworking

@paradaernesto
Copy link

Hello, I had the same error.
I added the next lines to fix this:

NSMutableDictionary *configuration = [NSMutableDictionary dictionaryWithDictionary:[[NXOAuth2AccountStore sharedStore] configurationForAccountType:@"fitbit"]];

NSString *authHeader = [NSString stringWithFormat:@"Basic %@", [[NSString stringWithFormat:@"%@:%@", kClientId, kClientSecretKey] base64EncodedString]];

[configuration setObject:@{@"Content-Type": @"application/x-www-form-urlencoded", @"Authorization": authHeader} forKey:kNXOAuth2AccountStoreConfigurationCustomHeaderFields];

[[NXOAuth2AccountStore sharedStore] setConfiguration:configuration forAccountType:@"fitbit"];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants