使用http4k OAuth模块实现Google登录时如何获取ID Token
使用http4k OAuth模块实现Google登录时获取ID Token的问题
我用http4k内置的OAuth模块在后端实现Google登录,已经完成以下基础配置:
val googleClientId = "<GoogleClientID>" val googleClientSecret = "<GoogleClientSecret>" // 测试用的OAuth持久化实现,实际需开发者自行实现 val oAuthPersistence = InsecureCookieBasedOAuthPersistence("Google") // 使用http4k预定义的Google OAuth配置 val oauthProvider = OAuthProvider.google( JavaHttpClient(), Credentials(googleClientId, googleClientSecret), Uri.of("http://localhost:9000/oauth/callback"), oAuthPersistence ) val app: HttpHandler = routes( "/oauth" bind routes( "/" bind GET to oauthProvider.authFilter.then { val user = contextFn(it) Response(OK).body("authenticated!") }, "/callback" bind GET to oauthProvider.callback ) app.asServer(SunHttp(9000)).start()
目前能正常访问http://localhost:9000/oauth完成Google账号登录,但登录重定向后进入contextFn函数时,仅能从Cookie中拿到GoogleCsrf、GoogleAccessToken和GoogleOriginalUri,无法获取ID Token。我的contextFn代码如下:
val transport = NetHttpTransport() val jsonFactory = GsonFactory.getDefaultInstance() val verifier = GoogleIdTokenVerifier.Builder(transport, jsonFactory) .setAudience(listOf(googleClientId)) .build() fun contextFn(request: Request): Principal { // TODO: 如何获取ID Token?当前请求Cookie中只有上述三个字段 val idTokenString = "" val idToken: GoogleIdToken = verifier.verify(idTokenString) val payload: GoogleIdToken.Payload = idToken.payload // 打印用户标识 val userId: String = payload.subject println("User ID: $userId") // 从payload中获取用户信息 val email: String = payload.email val emailVerified: Boolean = payload.emailVerified val name = payload["name"] return GoogleUser(email) }
请问如何获取ID Token?目前只能拿到access token。
解决方法
要获取ID Token,需要调整http4k的OAuth配置,满足两个核心条件:Google OAuth要求的openid scope,以及让http4k保存ID Token到持久化存储中。
1. 自定义Google OAuth配置,添加必要的Scope
http4k默认的OAuthProvider.google()没有包含openid scope,而Google只有在请求包含openid时才会返回ID Token。我们需要自定义配置并添加所需的scope:
val googleOAuthConfig = OAuthProviderConfig( authUri = Uri.of("https://accounts.google.com/o/oauth2/v2/auth"), tokenUri = Uri.of("https://oauth2.googleapis.com/token"), credentials = Credentials(googleClientId, googleClientSecret), callbackUri = Uri.of("http://localhost:9000/oauth/callback"), scope = listOf("openid", "email", "profile") // 必须包含openid,其余可按需添加 )
2. 创建OAuthProvider时启用ID Token保存
使用自定义配置创建OAuthProvider,并显式设置TokenRetriever的includeIdToken为true,这样http4k会把ID Token保存到持久化存储中:
val oauthProvider = OAuthProvider( client = JavaHttpClient(), config = googleOAuthConfig, persistence = oAuthPersistence, tokenRetriever = TokenRetriever.Default(includeIdToken = true) )
3. 从持久化存储中读取ID Token
修改contextFn,通过OAuthPersistence的retrieveToken方法获取完整的OAuthToken对象,其中包含idToken字段:
fun contextFn(request: Request): Principal { // 从持久化中获取包含ID Token的OAuthToken对象 val oAuthToken = oAuthPersistence.retrieveToken(request) ?: throw IllegalArgumentException("未找到OAuth令牌") val idTokenString = oAuthToken.idToken ?: throw IllegalArgumentException("ID Token不存在") val idToken: GoogleIdToken = verifier.verify(idTokenString) val payload: GoogleIdToken.Payload = idToken.payload val userId: String = payload.subject println("User ID: $userId") val email: String = payload.email val emailVerified: Boolean = payload.emailVerified val name = payload["name"] return GoogleUser(email) }
关键说明
- OpenID Connect要求:必须在scope中包含
openid,否则Google不会返回ID Token,这是OpenID协议的强制要求。 - http4k配置细节:默认的
TokenRetriever不会保存ID Token,必须显式开启includeIdToken = true,才能让持久化存储保存ID Token。 - 持久化读取:
InsecureCookieBasedOAuthPersistence会把完整的OAuthToken(包含idToken)存在Cookie中,通过retrieveToken即可取出。
内容的提问来源于stack exchange,提问作者Arnab Datta
相关产品推荐
相关产品推荐

