OAauth2を使ったAPIでエラーになる


# 事象 : Bad credentials
- 環境
  - Windows10 Pro バージョン1909
  - spring-security-core\3.2.10
  - spring-security-oauth2\2.3.3

```bash
$ curl -X POST -H 'Authorization:Basic A...==' http://localhost:8080/api/oauth/token?grant_type=password&username=username&password=password
[1] 1819
[2] 1820
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0[2]+  Done                    username=username
100    63    0    63    0     0      3      0 --:--:--  0:00:20 --:--:--    14{"error":"invalid_grant","error_description":"Bad credentials"}
```

## 原因 : 不明

`Bad credentials`っていってるから証明書かナンかなと思って`-k`オプション付けてみても変わらず・・・・せっせとデバックしたけど時間切れ

1. security.oauth2.provider.CompositeTokenGranter#grant
1. security.oauth2.provider.token.AbstractTokenGranter#grant
1. security.oauth2.provider.token.AbstractTokenGranter#getAccessToken
1. security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter#getOAuth2Authentication
1. security.authentication.ProviderManager#authenticate
1. security.authentication.dao.AbstractUserDetailsAuthenticationProvider#afterPropertiesSet
1. security.authentication.dao.DaoAuthenticationProvider#retrieveUser

```java:AbstractUserDetailsAuthenticationProvider#afterPropertiesSet
// principalが「null」でusernameが「NONE_PROVIDED」になる
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();

// ...省略...
    try {
        // usernameが「NONE_PROVIDED」でretrieveUserからUsernameNotFoundExceptionが帰ってくる
        user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
    } catch (UsernameNotFoundException notFound) {
        logger.debug("User '" + username + "' not found");
        // ...省略...
```

### 諦めた対応 : Postmanをつかう
Postmanで同じヘッダとパラメータにするとうまくいった。
[Settings]の[Enable SSL certificate verification]は「OFF」にしてある・・・「ON」にしてもうまくいく・・・なんだろう？


# 事象 : Missing grant type
- 環境
  - Windows10 Pro バージョン1909
  - spring-security-core\3.2.10
  - spring-security-oauth2\2.3.3

```bash
$ curl -X POST -H 'Authorization:Basic A...==' http://localhost:8080/api/oauth/token?username=username&password=password
[1] 1778
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100    68    0    68    0     0      3      0 --:--:--  0:00:20 --:--:--    17{"error":"invalid_request","error_description":"Missing grant type"}

```

## 原因 : grant_typeを指定してないから

以下表は[&#12504;&#12523;&#12503; | &#12488;&#12524;&#12540;&#12491;&#12531;&#12464; | Salesforce](https://help.salesforce.com/articleView?id=remoteaccess_oauth_username_password_flow.htm&type=5)より引用

|パラメータ|説明|
|:--|:--|
|grant_type|接続アプリケーションが要求する OAuth 2.0 許可種別。このフローの値は password である必要があります。|
|client_id|接続アプリケーションのコンシューマ鍵。接続アプリケーションの [接続アプリケーションを管理する] ページまたは接続アプリケーションの定義から検索できます。|
|client_secret|接続アプリケーションのコンシューマの秘密。接続アプリケーションの [接続アプリケーションを管理する] ページまたは接続アプリケーションの定義から検索できます。|
|username|接続アプリケーションが模倣するユーザのユーザ名。|
|password|接続アプリケーションが模倣するユーザのパスワード。|

デバックすると`tokenRequest.getGrantType()`が「null」でみごとExceptionになっている

```java:org.springframework.security.oauth2.provider.endpoint.TokenEndpoint
  @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
  public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
  Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
// ...省略...
    if (!StringUtils.hasText(tokenRequest.getGrantType())) {
      throw new InvalidRequestException("Missing grant type");
    }
```

### 対応 : パラメータの最初にgrant_typeをくっつける

```bash
# 「grant_type=password」をパラメータの最後につけたらだめだった
$ curl ...&password=password&grant_type=password
...
...{"error":"invalid_request","error_description":"Missing grant type"}

# 「grant_type=password」をパラメータの最初につけたらいけた(まだエラーだけど)
$ curl -X POST -H 'Authorization:Basic A...==' http://localhost:8080/api/oauth/token?grant_type=password&username=username&password=password
...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0[2]+  Done                    username=username
100    63    0    63    0     0      3      0 --:--:--  0:00:20 --:--:--    14{"error":"invalid_grant","error_description":"Bad credentials"}
```