web - 如何从 Google Identity Aware Proxy 后面的 Web 应用程序访问已通过身份验证的用户?
问题描述
我有一个位于 Google 身份识别代理 (IAP) 后面的网络应用程序。IAP 在转发到我的 Web 应用程序之前对用户进行身份验证。如何从我的 Web 应用程序访问已通过身份验证的用户?
在获取用户的身份中,它声明有X-Goog-Authenticated-User-Email
和X-Goog-Authenticated-User-Id
标题。但是,我在响应标头中看不到那些:
accept-ranges: bytes
alt-svc: clear
content-length: 14961
content-type: text/html; charset=utf-8
date: Thu, 01 Apr 2021 15:21:01 GMT
last-modified: Wed, 31 Mar 2021 19:34:58 GMT
via: 1.1 google
我确实看到了一些 cookie:
GCP_IAAP_AUTH_TOKEN_xxx
GCP_IAP_UID
GCP_IAP_XSRF_NONCE_xxx
例如,我希望能够在我的网络应用程序中显示他们的姓名和头像照片,以表明他们已通过身份验证并已登录。我知道这些信息可通过 Google 的 OAuth2 结构获得,但我如何从 IAP 获取这些信息?
解决方案
在@JohnHanley 提到只有在 IAP 后面运行时才会显示标头后,我才能够完成这项工作。在本地开发过程中您看不到它们。
在部署一个简单的临时路由后,我可以看到它们,/headers
该路由循环通过它们并写入 ResponseWriter。 X-Goog-Authenticated-User-Id
,X-Goog-Authenticated-User-Email
和X-Goog-Iap-Jwt-Assertion
.
import (
"fmt"
"net/http"
"github.com/rs/zerolog/log"
)
func headersHandler(w http.ResponseWriter, r *http.Request) {
log.Info().Msg("Entering headersHandler")
fmt.Fprintf(w, "Request Headers\n\n")
log.Debug().Msg("Request Headers:")
for name, values := range r.Header {
log.Debug().Interface(name, values).Send()
fmt.Fprintf(w, "%s = %s\n", name, values)
}
}
这是一条临时路线。一旦我可以确认标题,我就删除了它。
此外,我必须为托管我的 Web 应用程序的 ProjectId 启用 Google 的People API。
之后,我使用 Go 包进行了测试google.golang.org/api/people/v1
,发现使用当前经过身份验证的用户的约定people/me
在我的情况下不起作用,因为它返回了正在使用的服务帐户。相反,我必须以编程方式填写用户 id people/userid
。然后它起作用了。
对于我的用例,我创建了一个/user
路由来返回用户信息的子集,即姓名、电子邮件、照片 url。
结构:
type GoogleUser struct {
Name string `json:"name"`
Email string `json:"email"`
PhotoUrl string `json:"photo_url"`
}
处理程序:
func userHandler(w http.ResponseWriter, r *http.Request) {
log.Info().Msg("Entering userHandler")
var err error
// Make sure this is a valid API request
// Request header Content-Type: application/json must be present
if !ValidAPIRequest(r) {
err = writeJSONError(w, ResponseStatusNotFound("Not found"))
if err != nil {
log.Error().Msg(err.Error())
}
return
}
// Extract user id from header
var userId string = r.Header.Get("X-Goog-Authenticated-User-Id")
if userId != "" {
userId = strings.ReplaceAll(userId, "accounts.google.com:", "")
}
// Extract user email from header
var userEmail string = r.Header.Get("X-Goog-Authenticated-User-Email")
if userEmail != "" {
userEmail = strings.ReplaceAll(userEmail, "accounts.google.com:", "")
}
// Get the currently authenticated Google user
googleUser, err := GetCurrentGoogleUser(userId, userEmail)
if err != nil {
log.Error().Msg(err.Error())
err = writeJSONError(w, ResponseStatusInternalError(err.Error()))
if err != nil {
log.Error().Msg(err.Error())
}
return
}
// Write the JSON response
err = writeJSONGoogleUser(w, http.StatusOK, &googleUser)
if err != nil {
log.Error().Msg(err.Error())
}
}
谷歌人员 API:
func GetCurrentGoogleUser(userId string, userEmail string) (GoogleUser, error) {
// Pre-conditions
if userId == "" {
return GoogleUser{}, errors.New("userId is blank")
}
if userEmail == "" {
return GoogleUser{}, errors.New("userEmail is blank")
}
log.Debug().
Str("userId", userId).
Str("userEmail", userEmail).
Send()
ctx := context.Background()
// Instantiate a new People service
peopleService, err := people.NewService(ctx, option.WithAPIKey(GoogleAPIKey))
if err != nil {
return GoogleUser{}, err
}
// Define the resource name using the user id
var resourceName string = fmt.Sprintf("people/%s", userId)
// Get the user profile
profile, err := peopleService.People.Get(resourceName).PersonFields("names,photos").Do()
if err != nil {
return GoogleUser{}, err
}
log.Debug().
Interface("profile", profile).
Send()
return GoogleUser{Name: profile.Names[0].DisplayName, Email: userEmail, PhotoUrl: profile.Photos[0].Url}, nil
}
推荐阅读
- android-ndk - Pixel/Pixel2 XL 上的 AAssetManager_open 死锁
- jquery - 更改 Json 数组的值并放入 Hiddenfield
- c# - 在事件中引发事件
- rust - 核心中的运算符真的是循环定义的吗?
- gremlin - 如何在 gremlin 的路径中检索超过 2 个属性。
- sql - 统计不同范围内的点数SQL
- python - 通过使用 pandas 在时间序列中的先前 NaN 之间分配值来回填值
- java - 获取 HttpServletRequest 到 Spring ResponseEntityExceptionHandler
- python - NumPy 2D 数组迭代速度
- python-3.x - how to index your tkinter labels?