You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 08:45:32