【问题标题】:How to access already authenticated user from web application behind Google Identity Aware Proxy?如何从 Google Identity Aware Proxy 后面的 Web 应用程序访问已通过身份验证的用户?
【发布时间】:2021-06-28 15:20:17
【问题描述】:

我有一个位于 Google 身份识别代理 (IAP) 后面的网络应用程序。 IAP 在转发到我的 Web 应用程序之前对用户进行身份验证。如何从我的 Web 应用程序访问已通过身份验证的用户?

在Getting the user's identity 中声明有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 结构获得,但我如何从内购?

【问题讨论】:

  • 标头 X-Goog-Authenticated-User-Email 作为请求标头从 IAP 发送到您的后端,而不是作为响应标头发送到客户端。客户端收到身份验证 cookie。
  • @JohnHanley 我也在某处读过。我正在添加一条日志消息以检查该值。有点乏味,因为这无法在本地开发机器上进行测试,并且必须部署到 IAP 所在的生产环境中。不过我会测试一下。谢谢!
  • 您在 IAP 背后使用的是哪个服务?我对新服务所做的一个技巧是从标题和环境变量创建一个 HTML 表,然后将其作为响应发送回来。这让我可以查看发送到 Cloud Run 或 App Engine 等服务的所有标头。
  • 我现在可以在部署一个简单的 /headers 路由后看到它们,该路由循环通过它们并写入 ResponseWriter。 X-Goog-Authenticated-User-Id、X-Goog-Authenticated-User-Email 和 X-Goog-Iap-Jwt-Assertion。谢谢@JohnHanley!

标签: web google-cloud-platform google-iap identity-aware-proxy google-cloud-identity-aware-proxy


【解决方案1】:

在@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。

之后,我使用 google.golang.org/api/people/v1 的 Go 包进行了测试,发现通过 people/me 使用当前经过身份验证的用户的约定在我的情况下不起作用,因为它返回了正在使用的服务帐户。相反,我必须以编程方式填写用户 ID people/userid。然后就成功了。

对于我的用例,我创建了一个/user 路由来返回用户信息的子集,即姓名、电子邮件、照片网址。

结构:

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
}

【讨论】:

    猜你喜欢
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    • 2021-03-04
    • 2015-11-08
    • 2017-05-09
    相关资源
    最近更新 更多