Flutter API Request BadState response

71 views Asked by At

I am currently trying to implement an API request with flutter. I have implemented the resuest in Postman and tried all the data. The result was successful. Then I tried to implement this request in my programme. However, I have a problem here, which is why I always get the ResponseCode 400. However, I can no longer explain why. I am using the FatSecret API here.

The code I used for this request requires an Authorisation Header according to the documentation. This consists of the clientID and the clientSecret. The body also contains "grant_type": "client_credentials" and "scope": "premier".

class FatSecretAPI extends ConsumerWidget {
  const FatSecretAPI({super.key});

final String clientId = "...";
final String clientSecret = "...";

  Future<FatSecretToken> fetchAccesToken() async {
    final response = await http
        .post(Uri.parse("https://oauth.fatsecret.com/connect/token"),
        body: {
      "grant_type": "client_credentials",
      "scope": "premier"
    }, headers: {
           HttpHeaders.authorizationHeader: "Basic ${base64.encode(utf8.encode('$clientId:$clientSecret'))};"
    });
    if (response.statusCode == 200) {
      var object = FatSecretToken.fromJson(
          jsonDecode(response.body) as Map<String, dynamic>);
      print(object.accessToken);
      return object;
    } else {
      print(response.statusCode);
      throw Exception("Failed to load token");
    }
  }

  Future<void> loadToken(WidgetRef ref) async {
    final token = await fetchAccesToken();
    ref.read(accesTokenProvider.notifier).state = token.accessToken;
  }
}

class FatSecretToken {
  final String accessToken;
  final String tokenType;
  final int expiresIn;

  FatSecretToken(
      {required this.accessToken,
      required this.tokenType,
      required this.expiresIn});

  factory FatSecretToken.fromJson(Map<String, dynamic> json) {
    return FatSecretToken(
      accessToken: json['access_token'],
      tokenType: json['token_type'],
      expiresIn: json['expires_in'],
    );
  }
}

A response in Json format should actually appear here, which contains information about the accesToken and the token type.

2

There are 2 answers

0
Pantastix On BEST ANSWER

I have reproduced the whole thing and this is how it works. Probably because of the ; in the auth header. This way it is also more readable and easily customisable for other purposes. You should also outsource the secrets so that they are more secure.

Future<FatSecretToken> getToken() async {
    final basicAuth =
        'Basic ' + base64Encode(utf8.encode('$clientId:$clientSecret'));

    final headers = <String, String>{
      'Authorization': basicAuth,
      // 'Content-Type': 'application/x-www-form-urlencoded',
    };

    final body = <String, String>{
      'grant_type': 'client_credentials',
      'scope': 'premier barcode',
    };

    final response = await http.post(
      Uri.parse('https://oauth.fatsecret.com/connect/token'),
      headers: headers,
      body: body,
    );

    if (response.statusCode == 200) {
      final object = FatSecretToken.fromJson(
          jsonDecode(response.body) as Map<String, dynamic>,
      );
      return object;
    } else {
      throw Exception('Failed to get token: ${response.reasonPhrase}');
    }
  }
1
Furkan Topaloglu On

The problem in your code is ";" It looks like.

You should update the 'headers' section as follows:

 headers: {
    HttpHeaders.authorizationHeader:
        "Basic ${base64.encode(utf8.encode('$clientId:$clientSecret'))}"
  },

Additionally, the "scope" value must be "basic"