- me := url.URL(cluster.Services.Controller.ExternalURL)
- callback, err := me.Parse("/" + arvados.EndpointLogin.Path)
- if err != nil {
- return ctrl.loginError(err)
- }
- conf.RedirectURL = callback.String()
- state := ctrl.newOAuth2State([]byte(cluster.SystemRootToken), opts.Remote, opts.ReturnTo)
- return arvados.LoginResponse{
- RedirectLocation: conf.AuthCodeURL(state.String(),
- // prompt=select_account tells Google
- // to show the "choose which Google
- // account" page, even if the client
- // is currently logged in to exactly
- // one Google account.
- oauth2.SetAuthURLParam("prompt", "select_account")),
- }, nil
- } else {
- // Callback after Google sign-in.
- state := ctrl.parseOAuth2State(opts.State)
- if !state.verify([]byte(cluster.SystemRootToken)) {
- return ctrl.loginError(errors.New("invalid OAuth2 state"))
- }
- oauth2Token, err := conf.Exchange(ctx, opts.Code)
- if err != nil {
- return ctrl.loginError(fmt.Errorf("error in OAuth2 exchange: %s", err))
- }
- rawIDToken, ok := oauth2Token.Extra("id_token").(string)
- if !ok {
- return ctrl.loginError(errors.New("error in OAuth2 exchange: no ID token in OAuth2 token"))
- }
- idToken, err := verifier.Verify(ctx, rawIDToken)
- if err != nil {
- return ctrl.loginError(fmt.Errorf("error verifying ID token: %s", err))
- }
- authinfo, err := ctrl.getAuthInfo(ctx, conf, oauth2Token, idToken)
- if err != nil {
- return ctrl.loginError(err)
- }
- ctxRoot := auth.NewContext(ctx, &auth.Credentials{Tokens: []string{cluster.SystemRootToken}})
- return railsproxy.UserSessionCreate(ctxRoot, rpc.UserSessionCreateOptions{
- ReturnTo: state.Remote + "," + state.ReturnTo,
- AuthInfo: *authinfo,
- })