HTTP Post oAuth 2 "Check the `grant_type` parameter"

HTTP Post oAuth 2 "Check the `grant_type` parameter"

我正在使用以下代码在我的 dart 应用程序中为 Wikimedia 实施 OAuth 2 授权流程:

String jsonString = jsonEncode(<String, String>{
  'grant_type' : 'authorization_code',
  'redirect_uri' : Uri.encodeFull(redirectUri),
  'code' : authCode,
  'client_id' : CLIENT_ID,
  'client_secret' : clientSecret,
});
String paramName = 'param';
String formBody = paramName + '=' + Uri.encodeQueryComponent(jsonString);
List<int> bodyBytes = utf8.encode(formBody);

Future<http.Response> response = http.post( 
  Uri.parse('https://meta.wikimedia.org/w/rest.php/oauth2/access_token'),
  headers: <String, String>{
    "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
    "Content-Length" : bodyBytes.length.toString()
  },
  body: bodyBytes,
);

对此的回应是:

{ "error": "invalid_request", "error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.",
"hint": "Check the `grant_type` parameter", "message": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed." }

这可能与 content-type 仍然是 JSON 这一事实有关,尽管我在 header 中将其定义为 application/x-www-form-urlencoded 或者因为 content-length 是-1.

Header Information from Flutter DevTools

General Information from Flutter DevTools

要匹配 Content-Type header,您需要将参数编码为 application/x-www-form-urlencoded

为此,您可以将代码更改为:

String body = Uri(queryParameters: <String, String> {
  'grant_type' : 'authorization_code',
  'redirect_uri' : Uri.encodeFull(redirectUri),
  'code' : authCode,
  'client_id' : CLIENT_ID,
  'client_secret' : clientSecret,
}).query;

调用几乎相同:

Future<http.Response> response = http.post( 
  Uri.parse('https://meta.wikimedia.org/w/rest.php/oauth2/access_token'),
  headers: <String, String>{
    'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
    'Content-Length' : body.length.toString()
  },  
  body: body,
);

附录

这可以更简洁一些,将两个部分合并到一个调用中:

Future<http.Response> response = http.post(
  Uri.parse('https://meta.wikimedia.org/w/rest.php/oauth2/access_token'),
  headers: <String, String>{
    'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
  },  
  body: <String, String>{
    'grant_type': 'authorization_code',
    'redirect_uri': Uri.encodeFull(redirectUri),
    'code': authCode,
    'client_id': CLIENT_ID,
    'client_secret': clientSecret,
  }
);

请注意,特定的 Content-Length header 不是必需的,因为这是在发送时计算的。