Search code examples
flutterdartoauth-2.0

How to avoid 401 Unauthorized response at the second step of the SUUNTO API OAuth2?


I'd like to integrate SUUNTO API into a Flutter application. I'm registered to the SUUNTO API Zone, I have my Client ID, API key, secret. I'm only in Developer subscription since I'm testing out the water. I can an authorization code with ease (step 4.4 at https://apizone.suunto.com/how-to-start) but then always slapped with a 401 Unauthorized when I try to use that code to obtain the JWT token (step 4.5).

This is actually mentioned in their FAQ https://apizone.suunto.com/faq, but I'm already using the Basic authentication as advised with my Client ID and secret.

const REDIRECT_URL = "suuntoflutter://redirect/";

  Future<SuuntoToken> _getSuuntoToken(
    String clientId,
    String secret,
    String code,
  ) async {
    final encodedRedirectUrl = Uri.encodeQueryComponent(REDIRECT_URL);
    final params = "?grant_type=authorization_code&code=$code&redirect_uri=$encodedRedirectUrl";
    final tokenRequestUrl = "https://cloudapi-oauth.suunto.com/oauth/token" + params;
    final base64String = base64.encode(utf8.encode("$clientId:$secret"));
    final tokenResponse = await http.post(Uri.parse(tokenRequestUrl), headers: {"Authorization": "Basic: $base64String"});

The reason I'm posting this because I wrote to SUUNTO but haven't got any response and I hope maybe someone can help here. Each fitness portal has some variations over the OAuth2, I have the Strava integration and Under Armour integration fully working now.

What I've tried with SUUNTO:

  1. Encode the grant_type, code, and redirect_uri parameters as form submission in the body instead of URL parameters. Some OAuth2 integrations do it that way. This didn't change anything.
  2. I changed my client secret just to be sure I'm using the right one. Didn't change anything.
  3. I tried to use my API Zone credentials instead of the ClientID:ClientSecret for the Basic authentication. This doesn't make much sense but I still tried by desperation, but it didn't change anything.
  4. I tried to use my SUUNTO end-user credentials instead of the ClientID:ClientSecret for the Basic authentication. This doesn't make any sense at all but I still tried by desperation, but it didn't change anything.

I ran out of ideas.


Solution

  • I went back to the beginning and was able to get a JWT token with the SUUNTO doc example curl command curl -v https://cloudapi-oauth.suunto.com/oauth/token --user <client id>:<client secret> -d grant_type=authorization_code -d redirect_uri=<redirect URI> -d code=<authorization code from step 4.4>.

    After that I went through a lot of variations again (sending the parameters form encoded or in the URL etc.) until I realized that it was just one rogue extra character in the Basic authentication header string interpolation: and extra unintended colon:

    "Basic $base64String" vs "Basic: $base64String". That extra colon was the problem.