OpenID Connectを用いたSSOのフローと実装
0. OAuth 2.0とOpenID Connect
用語
- OAuth Provider : 認可を提供する側(ex. Twitter, Google)
- ソーシャルログインのことを SSO(Single Sign On) とも言う
- OAuthに認証の機能を足したものがOpenID Connect
OAuth.io,Auth0,Firebase Authenticationなどの OAuth Providerなどをサービスとして提供しているものを IDaaS という- アクセストークンやユーザー情報をエンコードしたものを ID Token という
IDトークンが分かれば OpenID Connect が分かる - Qiita
OAuth は認証のためのプロトコルではない
→ Open ID Connectを使うべきだがTwitterとかは対応していないので仕方なくOAuthを使っている
OAuth 認証を真面目に考える | DevelopersIO
APIクライアントでOAuth認可でソーシャルログインを行い、返ってきたアクセストークン, ユーザー情報をAPIサーバーに送り, 検証してもらうことで認証を行うのは自然らしい
一番分かりやすい OpenID Connect の説明 - Qiita
1. OpenID ConnectによるSSOのフロー
@startuml
autonumber
participant Client
participant "API Server"
participant OAuth.io
participant "OAuth provider"
group OAuthIO.popup({providerId})
Client -> "OAuth provider": social login request
"OAuth provider" -> Client: show login form
Client -> "OAuth provider": login
"OAuth provider" -> OAuth.io: authentication code
OAuth.io -> "OAuth provider": request access token
"OAuth provider" -> OAuth.io: access token & idToken
Client <- OAuth.io: response IdToken
end
group API /login/{providerId}
Client -> "API Server": send IdToken to /login/{providerId}
"API Server" -> "API Server": verify IdToken
note over "API Server"
create or fetch user info
end note
"API Server" -> Client: user info & api access token as JWT
end
@endumlOpen ID Connectによる認証がクライアント側で完了するとクライアント側にIdTokenが返ってくるのでそれをAPIサーバーに送って検証することで認証とする
- ユーザーがサインイン, サインアップボタンを押す
- OAuth.ioがOAuth Providerにリクエストを送る
- callback URLで指定したURL(=OAuth.io)に認証情報が返る
- OAuth.ioはクライアントにIdToken含むユーザー情報を返す
- APIサーバーにIdTokenをPOSTする
- APIサーバーはIdTokenの検証をする
- IdTokenが妥当ならユーザー情報が取得できる(正確にはIdTokenをデコードしてユーザー情報を取り出している)
- ユーザー情報とJWTを返却する
2. OAuth.io
node.jsとかで利用できるOAuth Consumerを代行してくれるサービス
GitHub - oauth-io/oauth-js: OAuth that just works ! This is the JavaScript SDK for OAuth.io
気になる点
- 開発がアクティブでない
- JS製で型定義が無い
- APIがコールバック関数の形
OAuth.popup() を使うには
を使用する.
OAuth.popup() するとコールバック関数でcredential OAuthIOCredential (IdTokenなど)が得られる. 型定義がないのでオブジェクトを見て型をあてる.
2.1 OAuth.ioセットアップ
-
oauth.io でDashBoardの
API Integration→Add Integrationから好きなProviderを選ぶ. -
client_idとclient_secretをOAuth Provider(GCP上で確認できる)で示されたものに設定しTry Authするとsocial loginを試すことができる. -
成功するとどんな情報が返ってくるか, クライアントのコード例が確認できる
-
クライアントに実装する
最小サンプル, oAuthIOPublicKey はOAuth.ioのダッシュボードから確認できる
import { OAuth } from 'oauthio-web'
const oAuthIOPublicKey = process.env.VUE_APP_OAUTHIO_PUBLIC_KEY;
OAuth.initialize(oAuthIOPublicKey);
OAuth.popup('google').done(async (cred) => {
// cred.id_token が IdToken
// APIサーバーにIdTokenをPOST
const res = await fetch('/api/users/register/google', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ idToken: cred.id_token })
})
})3. Google
3.1 認証情報を作成する
GCPでOAuthクライアントの認証情報を作成する
GoogleAPI、OAuth2.0の有効化の手順 - サーバーワークスエンジニアブログ
3.2 GoogleのIdTokenの内容
sub, email, picture をclaimとしてもらえる
sub: は一意, 文字列長22
OpenID Connect | Google Identity | Google Developers
3.3 APIサーバーでのIdTokenの検証
Java製のGoogle API Clientがある
API Client Library for Java | Google Developers
Authenticate with a backend server | Google Sign-In for Websites
repositories {
google()
}
dependencies {
implementation('com.google.api-client:google-api-client:1.32.2')
}API Clientを用いてIdTokenの検証を行う例
fun verifyIdToken(idToken: String): GoogleUserProfile {
val transport = NetHttpTransport()
val jsonFactory = GsonFactory()
val verifier = GoogleIdTokenVerifier.Builder(transport, jsonFactory)
// OAuth Consumer の clientId
.setAudience(listOf(clientId))
.build()
val decodedIdToken = verifier.verify(idToken)
// IdTokenからGoogleユーザー情報を作成
return GoogleUserProfile.of(decodedIdToken.payload)
}fun createUserWithGoogle(idToken: String) {
// トークンの検証
val profile = kotlin.runCatching {
googleTokenVerifier.verifyIdToken(requestDto.idToken)
}
.onFailure { throw InvalidPasswordException() }
.getOrThrow()
// profile に sub, email, picture が入っているのでそれらを元に User を作成
val user = User.from(profile)
}GCP: Google OAuth2 認証情報
4. Twitter
todo :Twitter Developer Programを申請しアプリとして作成しなければならない