OAuth2.0の「PKCEデータ」ってなに？


PKCE (Proof Key for Code Exchange) extension to OAuth 2.0

図で分かりやすいと思います。

```plantuml
@startuml
actor User
participant "Client\nApp" as NativeClient
participant "WebView" as WebView
participant "Authorization\nServer" as AuthServer
participant "Resource\nServer" as ResourceServer

NativeClient -> NativeClient: Generate\n<font color=red>code_verifier=Random 32 bytes</font> and\n<font color=red>code_challenge=SHA256(code_verifier)</font>
NativeClient -> WebView++: Open WebView\nand pass challenge
WebView -> AuthServer: Authorization Request\n(GET /authorize?response_type=code&client_id=...&<font color=red>code_challenge=...&code_challenge_method=S256</font>&<font color=magenta>redirect_uri</font>=...)
AuthServer -> AuthServer: Store <font color=red>code_challenge</font> and\n<font color=red>code_challenge_method</font>
AuthServer --> WebView: 302 redirect to login form page
WebView --> User: Show login form
User -> WebView: Enter credentials\nand submit
WebView -> AuthServer: Send credentials
AuthServer --> WebView: Display consent screen
WebView --> User: Show consend screen
User -> WebView: Approve consent
WebView -> AuthServer: Approve consent
AuthServer -> WebView: Redirect to <font color=magenta>redirect_uri</font>\nwith authorization <font color=blue>code</font> (302 Redirect)
WebView -> NativeClient: Provide authorization <font color=blue>code</font>
deactivate WebView
NativeClient -> AuthServer: Access Token Request\n(POST /token with <font color=blue>code</font>, <font color=red>code_verifier</font>, <font color=magenta>redirect_uri</font>, ...)
AuthServer -> AuthServer: Verify <font color=red>code_verifier</font>\nmatches <font color=red>code_challenge</font>
AuthServer --> NativeClient: Access Token Response\n(including <b>access_token</b>, token_type, expires_in, refresh_token)
NativeClient -> ResourceServer: Request Resource\n(with <b>access_token</b>)
ResourceServer --> NativeClient: Resource Response
@enduml
```

### 簡単な説明
クライアント側であらかじめ`code_verifier`と`code_challenge`を作っておく。
OAuth2.0の認証API(`/authorize`)に`code_challenge`を与える。
トークン交換API(`/token`)で`code_verifier`を与える。
そうすると、認証サーバー側で 検証できて不正検知できる。



## `code_verifier`の作成

code_verifierはhigh-entropy cryptographic stringであることが期待。要は、セキュアな乱数関数を使って32bytesの長さのものを作ればよい。以下はおもちゃ的な実装だがイメージしやすい。


Swift実装
```swift
func generateCodeVerifier() -> String {
    let length = 32
    var randomBytes = [UInt8](repeating: 0, count: length)
    let result = SecRandomCopyBytes(kSecRandomDefault, length, &randomBytes)
    if result != errSecSuccess {
        fatalError("生成できまちぇんでちた")
    }
    let codeVerifier = Data(randomBytes).base64EncodedString()
    return codeVerifier
}
```

## `code_challenge`の作成

code_verifierから生成する。code_verifierをそのもの出してもいいらしいけどセキュリティ的に弱くなってしまうので、SHA256で生成するのは一般的。/authorize APIで`code_challenge_method=S256`渡したしね。

Swift実装

```swift
import Foundation
import CommonCrypto

func generateCodeChallenge(from codeVerifier: String) -> String? {
    guard let data = codeVerifier.data(using: .utf8) else { return nil }
    var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
    data.withUnsafeBytes {
        _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
    }
    return Data(hash).base64EncodedString()
}
```

## 上記のシーケンス図

PlantUMLないとね


```java
@startuml
actor User
participant "Client\nApp" as NativeClient
participant "WebView" as WebView
participant "Authorization\nServer" as AuthServer
participant "Resource\nServer" as ResourceServer

NativeClient -> NativeClient: Generate\n<font color=red>code_verifier=Random 32 bytes</font> and\n<font color=red>code_challenge=SHA256(code_verifier)</font>
NativeClient -> WebView++: Open WebView\nand pass challenge
WebView -> AuthServer: Authorization Request\n(GET /authorize?response_type=code&client_id=...&<font color=red>code_challenge=...&code_challenge_method=S256</font>&<font color=magenta>redirect_uri</font>=...)
AuthServer -> AuthServer: Store <font color=red>code_challenge</font> and\n<font color=red>code_challenge_method</font>
AuthServer --> WebView: 302 redirect to login form page
WebView --> User: Show login form
User -> WebView: Enter credentials\nand submit
WebView -> AuthServer: Send credentials
AuthServer --> WebView: Display consent screen
WebView --> User: Show consend screen
User -> WebView: Approve consent
WebView -> AuthServer: Approve consent
AuthServer -> WebView: Redirect to <font color=magenta>redirect_uri</font>\nwith authorization <font color=blue>code</font> (302 Redirect)
WebView -> NativeClient: Provide authorization <font color=blue>code</font>
deactivate WebView
NativeClient -> AuthServer: Access Token Request\n(POST /token with <font color=blue>code</font>, <font color=red>code_verifier</font>, <font color=magenta>redirect_uri</font>, ...)
AuthServer -> AuthServer: Verify <font color=red>code_verifier</font>\nmatches <font color=red>code_challenge</font>
AuthServer --> NativeClient: Access Token Response\n(including <b>access_token</b>, token_type, expires_in, refresh_token)
NativeClient -> ResourceServer: Request Resource\n(with <b>access_token</b>)
ResourceServer --> NativeClient: Resource Response
@enduml
```

以上です。

役にたつといいですね。