1package server
2
3import (
4	"context"
5	"encoding/json"
6	"errors"
7	"fmt"
8	"net/http"
9	"net/url"
10	"path"
11	"sort"
12	"strconv"
13	"strings"
14	"sync"
15	"time"
16
17	"github.com/gorilla/mux"
18	"golang.org/x/crypto/bcrypt"
19	jose "gopkg.in/square/go-jose.v2"
20
21	"github.com/concourse/dex/connector"
22	"github.com/concourse/dex/server/internal"
23	"github.com/concourse/dex/storage"
24)
25
26// newHealthChecker returns the healthz handler. The handler runs until the
27// provided context is canceled.
28func (s *Server) newHealthChecker(ctx context.Context) http.Handler {
29	h := &healthChecker{s: s}
30
31	// Perform one health check synchronously so the returned handler returns
32	// valid data immediately.
33	h.runHealthCheck()
34
35	go func() {
36		for {
37			select {
38			case <-ctx.Done():
39				return
40			case <-time.After(time.Second * 15):
41			}
42			h.runHealthCheck()
43		}
44	}()
45	return h
46}
47
48// healthChecker periodically performs health checks on server dependenices.
49// Currently, it only checks that the storage layer is avialable.
50type healthChecker struct {
51	s *Server
52
53	// Result of the last health check: any error and the amount of time it took
54	// to query the storage.
55	mu sync.RWMutex
56	// Guarded by the mutex
57	err    error
58	passed time.Duration
59}
60
61// runHealthCheck performs a single health check and makes the result available
62// for any clients performing and HTTP request against the healthChecker.
63func (h *healthChecker) runHealthCheck() {
64	t := h.s.now()
65	err := checkStorageHealth(h.s.storage, h.s.now)
66	passed := h.s.now().Sub(t)
67	if err != nil {
68		h.s.logger.Errorf("Storage health check failed: %v", err)
69	}
70
71	// Make sure to only hold the mutex to access the fields, and not while
72	// we're querying the storage object.
73	h.mu.Lock()
74	h.err = err
75	h.passed = passed
76	h.mu.Unlock()
77}
78
79func checkStorageHealth(s storage.Storage, now func() time.Time) error {
80	a := storage.AuthRequest{
81		ID:       storage.NewID(),
82		ClientID: storage.NewID(),
83
84		// Set a short expiry so if the delete fails this will be cleaned up quickly by garbage collection.
85		Expiry: now().Add(time.Minute),
86	}
87
88	if err := s.CreateAuthRequest(a); err != nil {
89		return fmt.Errorf("create auth request: %v", err)
90	}
91	if err := s.DeleteAuthRequest(a.ID); err != nil {
92		return fmt.Errorf("delete auth request: %v", err)
93	}
94	return nil
95}
96
97func (h *healthChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
98	h.mu.RLock()
99	err := h.err
100	t := h.passed
101	h.mu.RUnlock()
102
103	if err != nil {
104		h.s.renderError(w, http.StatusInternalServerError, "Health check failed.")
105		return
106	}
107	fmt.Fprintf(w, "Health check passed in %s", t)
108}
109
110func (s *Server) handlePublicKeys(w http.ResponseWriter, r *http.Request) {
111	// TODO(ericchiang): Cache this.
112	keys, err := s.storage.GetKeys()
113	if err != nil {
114		s.logger.Errorf("failed to get keys: %v", err)
115		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
116		return
117	}
118
119	if keys.SigningKeyPub == nil {
120		s.logger.Errorf("No public keys found.")
121		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
122		return
123	}
124
125	jwks := jose.JSONWebKeySet{
126		Keys: make([]jose.JSONWebKey, len(keys.VerificationKeys)+1),
127	}
128	jwks.Keys[0] = *keys.SigningKeyPub
129	for i, verificationKey := range keys.VerificationKeys {
130		jwks.Keys[i+1] = *verificationKey.PublicKey
131	}
132
133	data, err := json.MarshalIndent(jwks, "", "  ")
134	if err != nil {
135		s.logger.Errorf("failed to marshal discovery data: %v", err)
136		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
137		return
138	}
139	maxAge := keys.NextRotation.Sub(s.now())
140	if maxAge < (time.Minute * 2) {
141		maxAge = time.Minute * 2
142	}
143
144	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d, must-revalidate", int(maxAge.Seconds())))
145	w.Header().Set("Content-Type", "application/json")
146	w.Header().Set("Content-Length", strconv.Itoa(len(data)))
147	w.Write(data)
148}
149
150type discovery struct {
151	Issuer        string   `json:"issuer"`
152	Auth          string   `json:"authorization_endpoint"`
153	Token         string   `json:"token_endpoint"`
154	Keys          string   `json:"jwks_uri"`
155	ResponseTypes []string `json:"response_types_supported"`
156	Subjects      []string `json:"subject_types_supported"`
157	IDTokenAlgs   []string `json:"id_token_signing_alg_values_supported"`
158	Scopes        []string `json:"scopes_supported"`
159	AuthMethods   []string `json:"token_endpoint_auth_methods_supported"`
160	Claims        []string `json:"claims_supported"`
161}
162
163func (s *Server) discoveryHandler() (http.HandlerFunc, error) {
164	d := discovery{
165		Issuer:      s.issuerURL.String(),
166		Auth:        s.absURL("/auth"),
167		Token:       s.absURL("/token"),
168		Keys:        s.absURL("/keys"),
169		Subjects:    []string{"public"},
170		IDTokenAlgs: []string{string(jose.RS256)},
171		Scopes:      []string{"openid", "email", "groups", "profile", "offline_access"},
172		AuthMethods: []string{"client_secret_basic"},
173		Claims: []string{
174			"aud", "email", "email_verified", "exp",
175			"iat", "iss", "locale", "name", "sub",
176		},
177	}
178
179	for responseType := range s.supportedResponseTypes {
180		d.ResponseTypes = append(d.ResponseTypes, responseType)
181	}
182	sort.Strings(d.ResponseTypes)
183
184	data, err := json.MarshalIndent(d, "", "  ")
185	if err != nil {
186		return nil, fmt.Errorf("failed to marshal discovery data: %v", err)
187	}
188
189	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
190		w.Header().Set("Content-Type", "application/json")
191		w.Header().Set("Content-Length", strconv.Itoa(len(data)))
192		w.Write(data)
193	}), nil
194}
195
196// handleAuthorization handles the OAuth2 auth endpoint.
197func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
198	authReq, err := s.parseAuthorizationRequest(r)
199	if err != nil {
200		s.logger.Errorf("Failed to parse authorization request: %v", err)
201		if handler, ok := err.Handle(); ok {
202			// client_id and redirect_uri checked out and we can redirect back to
203			// the client with the error.
204			handler.ServeHTTP(w, r)
205			return
206		}
207
208		// Otherwise render the error to the user.
209		//
210		// TODO(ericchiang): Should we just always render the error?
211		s.renderError(w, err.Status(), err.Error())
212		return
213	}
214
215	// TODO(ericchiang): Create this authorization request later in the login flow
216	// so users don't hit "not found" database errors if they wait at the login
217	// screen too long.
218	//
219	// See: https://github.com/dexidp/dex/issues/646
220	authReq.Expiry = s.now().Add(s.authRequestsValidFor)
221	if err := s.storage.CreateAuthRequest(authReq); err != nil {
222		s.logger.Errorf("Failed to create authorization request: %v", err)
223		s.renderError(w, http.StatusInternalServerError, "Failed to connect to the database.")
224		return
225	}
226
227	connectors, e := s.storage.ListConnectors()
228	if e != nil {
229		s.logger.Errorf("Failed to get list of connectors: %v", err)
230		s.renderError(w, http.StatusInternalServerError, "Failed to retrieve connector list.")
231		return
232	}
233
234	if len(connectors) == 1 {
235		for _, c := range connectors {
236			// TODO(ericchiang): Make this pass on r.URL.RawQuery and let something latter
237			// on create the auth request.
238			http.Redirect(w, r, s.absPath("/auth", c.ID)+"?req="+authReq.ID, http.StatusFound)
239			return
240		}
241	}
242
243	connectorInfos := make([]connectorInfo, len(connectors))
244	i := 0
245	for _, conn := range connectors {
246		connectorInfos[i] = connectorInfo{
247			ID:   conn.ID,
248			Name: conn.Name,
249			// TODO(ericchiang): Make this pass on r.URL.RawQuery and let something latter
250			// on create the auth request.
251			URL: s.absPath("/auth", conn.ID) + "?req=" + authReq.ID,
252		}
253		i++
254	}
255
256	if err := s.templates.login(w, connectorInfos); err != nil {
257		s.logger.Errorf("Server template error: %v", err)
258	}
259}
260
261func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
262	connID := mux.Vars(r)["connector"]
263	conn, err := s.getConnector(connID)
264	if err != nil {
265		s.logger.Errorf("Failed to create authorization request: %v", err)
266		s.renderError(w, http.StatusBadRequest, "Requested resource does not exist")
267		return
268	}
269
270	authReqID := r.FormValue("req")
271
272	authReq, err := s.storage.GetAuthRequest(authReqID)
273	if err != nil {
274		s.logger.Errorf("Failed to get auth request: %v", err)
275		if err == storage.ErrNotFound {
276			s.renderError(w, http.StatusBadRequest, "Login session expired.")
277		} else {
278			s.renderError(w, http.StatusInternalServerError, "Database error.")
279		}
280		return
281	}
282
283	// Set the connector being used for the login.
284	if authReq.ConnectorID != connID {
285		updater := func(a storage.AuthRequest) (storage.AuthRequest, error) {
286			a.ConnectorID = connID
287			return a, nil
288		}
289		if err := s.storage.UpdateAuthRequest(authReqID, updater); err != nil {
290			s.logger.Errorf("Failed to set connector ID on auth request: %v", err)
291			s.renderError(w, http.StatusInternalServerError, "Database error.")
292			return
293		}
294	}
295
296	scopes := parseScopes(authReq.Scopes)
297	showBacklink := len(s.connectors) > 1
298
299	switch r.Method {
300	case http.MethodGet:
301		switch conn := conn.Connector.(type) {
302		case connector.CallbackConnector:
303			// Use the auth request ID as the "state" token.
304			//
305			// TODO(ericchiang): Is this appropriate or should we also be using a nonce?
306			callbackURL, err := conn.LoginURL(scopes, s.absURL("/callback"), authReqID)
307			if err != nil {
308				s.logger.Errorf("Connector %q returned error when creating callback: %v", connID, err)
309				s.renderError(w, http.StatusInternalServerError, "Login error.")
310				return
311			}
312			http.Redirect(w, r, callbackURL, http.StatusFound)
313		case connector.PasswordConnector:
314			if err := s.templates.password(w, r.URL.String(), "", usernamePrompt(conn), false, showBacklink); err != nil {
315				s.logger.Errorf("Server template error: %v", err)
316			}
317		case connector.SAMLConnector:
318			action, value, err := conn.POSTData(scopes, authReqID)
319			if err != nil {
320				s.logger.Errorf("Creating SAML data: %v", err)
321				s.renderError(w, http.StatusInternalServerError, "Connector Login Error")
322				return
323			}
324
325			// TODO(ericchiang): Don't inline this.
326			fmt.Fprintf(w, `<!DOCTYPE html>
327			  <html lang="en">
328			  <head>
329			    <meta http-equiv="content-type" content="text/html; charset=utf-8">
330			    <title>SAML login</title>
331			  </head>
332			  <body>
333			    <form method="post" action="%s" >
334				    <input type="hidden" name="SAMLRequest" value="%s" />
335				    <input type="hidden" name="RelayState" value="%s" />
336			    </form>
337				<script>
338				    document.forms[0].submit();
339				</script>
340			  </body>
341			  </html>`, action, value, authReqID)
342		default:
343			s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
344		}
345	case http.MethodPost:
346		passwordConnector, ok := conn.Connector.(connector.PasswordConnector)
347		if !ok {
348			s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
349			return
350		}
351
352		username := r.FormValue("login")
353		password := r.FormValue("password")
354
355		identity, ok, err := passwordConnector.Login(r.Context(), scopes, username, password)
356		if err != nil {
357			s.logger.Errorf("Failed to login user: %v", err)
358			s.renderError(w, http.StatusInternalServerError, "Login error.")
359			return
360		}
361		if !ok {
362			if err := s.templates.password(w, r.URL.String(), username, usernamePrompt(passwordConnector), true, showBacklink); err != nil {
363				s.logger.Errorf("Server template error: %v", err)
364			}
365			return
366		}
367		redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
368		if err != nil {
369			s.logger.Errorf("Failed to finalize login: %v", err)
370			s.renderError(w, http.StatusInternalServerError, "Login error.")
371			return
372		}
373
374		http.Redirect(w, r, redirectURL, http.StatusSeeOther)
375	default:
376		s.renderError(w, http.StatusBadRequest, "Unsupported request method.")
377	}
378}
379
380func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request) {
381	var authID string
382	switch r.Method {
383	case http.MethodGet: // OAuth2 callback
384		if authID = r.URL.Query().Get("state"); authID == "" {
385			s.renderError(w, http.StatusBadRequest, "User session error.")
386			return
387		}
388	case http.MethodPost: // SAML POST binding
389		if authID = r.PostFormValue("RelayState"); authID == "" {
390			s.renderError(w, http.StatusBadRequest, "User session error.")
391			return
392		}
393	default:
394		s.renderError(w, http.StatusBadRequest, "Method not supported")
395		return
396	}
397
398	authReq, err := s.storage.GetAuthRequest(authID)
399	if err != nil {
400		if err == storage.ErrNotFound {
401			s.logger.Errorf("Invalid 'state' parameter provided: %v", err)
402			s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
403			return
404		}
405		s.logger.Errorf("Failed to get auth request: %v", err)
406		s.renderError(w, http.StatusInternalServerError, "Database error.")
407		return
408	}
409
410	if connID := mux.Vars(r)["connector"]; connID != "" && connID != authReq.ConnectorID {
411		s.logger.Errorf("Connector mismatch: authentication started with id %q, but callback for id %q was triggered", authReq.ConnectorID, connID)
412		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
413		return
414	}
415
416	conn, err := s.getConnector(authReq.ConnectorID)
417	if err != nil {
418		s.logger.Errorf("Failed to get connector with id %q : %v", authReq.ConnectorID, err)
419		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
420		return
421	}
422
423	var identity connector.Identity
424	switch conn := conn.Connector.(type) {
425	case connector.CallbackConnector:
426		if r.Method != http.MethodGet {
427			s.logger.Errorf("SAML request mapped to OAuth2 connector")
428			s.renderError(w, http.StatusBadRequest, "Invalid request")
429			return
430		}
431		identity, err = conn.HandleCallback(parseScopes(authReq.Scopes), r)
432	case connector.SAMLConnector:
433		if r.Method != http.MethodPost {
434			s.logger.Errorf("OAuth2 request mapped to SAML connector")
435			s.renderError(w, http.StatusBadRequest, "Invalid request")
436			return
437		}
438		identity, err = conn.HandlePOST(parseScopes(authReq.Scopes), r.PostFormValue("SAMLResponse"), authReq.ID)
439	default:
440		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
441		return
442	}
443
444	if err != nil {
445		s.logger.Errorf("Failed to authenticate: %v", err)
446		s.renderError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to authenticate: %v", err))
447		return
448	}
449
450	redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
451	if err != nil {
452		s.logger.Errorf("Failed to finalize login: %v", err)
453		s.renderError(w, http.StatusInternalServerError, "Login error.")
454		return
455	}
456
457	http.Redirect(w, r, redirectURL, http.StatusSeeOther)
458}
459
460// finalizeLogin associates the user's identity with the current AuthRequest, then returns
461// the approval page's path.
462func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.AuthRequest, conn connector.Connector) (string, error) {
463	claims := storage.Claims{
464		UserID:        identity.UserID,
465		Username:      identity.Username,
466		Name:          identity.Name,
467		Email:         identity.Email,
468		EmailVerified: identity.EmailVerified,
469		Groups:        identity.Groups,
470	}
471
472	updater := func(a storage.AuthRequest) (storage.AuthRequest, error) {
473		a.LoggedIn = true
474		a.Claims = claims
475		a.ConnectorData = identity.ConnectorData
476		return a, nil
477	}
478	if err := s.storage.UpdateAuthRequest(authReq.ID, updater); err != nil {
479		return "", fmt.Errorf("failed to update auth request: %v", err)
480	}
481
482	email := claims.Email
483	if !claims.EmailVerified {
484		email = email + " (unverified)"
485	}
486
487	s.logger.Infof("login successful: connector %q, username=%q, email=%q, groups=%q",
488		authReq.ConnectorID, claims.Username, email, claims.Groups)
489
490	return path.Join(s.issuerURL.Path, "/approval") + "?req=" + authReq.ID, nil
491}
492
493func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
494	authReq, err := s.storage.GetAuthRequest(r.FormValue("req"))
495	if err != nil {
496		s.logger.Errorf("Failed to get auth request: %v", err)
497		s.renderError(w, http.StatusInternalServerError, "Database error.")
498		return
499	}
500	if !authReq.LoggedIn {
501		s.logger.Errorf("Auth request does not have an identity for approval")
502		s.renderError(w, http.StatusInternalServerError, "Login process not yet finalized.")
503		return
504	}
505
506	switch r.Method {
507	case http.MethodGet:
508		if s.skipApproval {
509			s.sendCodeResponse(w, r, authReq)
510			return
511		}
512		client, err := s.storage.GetClient(authReq.ClientID)
513		if err != nil {
514			s.logger.Errorf("Failed to get client %q: %v", authReq.ClientID, err)
515			s.renderError(w, http.StatusInternalServerError, "Failed to retrieve client.")
516			return
517		}
518		if err := s.templates.approval(w, authReq.ID, authReq.Claims.Username, client.Name, authReq.Scopes); err != nil {
519			s.logger.Errorf("Server template error: %v", err)
520		}
521	case http.MethodPost:
522		if r.FormValue("approval") != "approve" {
523			s.renderError(w, http.StatusInternalServerError, "Approval rejected.")
524			return
525		}
526		s.sendCodeResponse(w, r, authReq)
527	}
528}
529
530func (s *Server) sendCodeResponse(w http.ResponseWriter, r *http.Request, authReq storage.AuthRequest) {
531	if s.now().After(authReq.Expiry) {
532		s.renderError(w, http.StatusBadRequest, "User session has expired.")
533		return
534	}
535
536	if err := s.storage.DeleteAuthRequest(authReq.ID); err != nil {
537		if err != storage.ErrNotFound {
538			s.logger.Errorf("Failed to delete authorization request: %v", err)
539			s.renderError(w, http.StatusInternalServerError, "Internal server error.")
540		} else {
541			s.renderError(w, http.StatusBadRequest, "User session error.")
542		}
543		return
544	}
545	u, err := url.Parse(authReq.RedirectURI)
546	if err != nil {
547		s.renderError(w, http.StatusInternalServerError, "Invalid redirect URI.")
548		return
549	}
550
551	var (
552		// Was the initial request using the implicit or hybrid flow instead of
553		// the "normal" code flow?
554		implicitOrHybrid = false
555
556		// Only present in hybrid or code flow. code.ID == "" if this is not set.
557		code storage.AuthCode
558
559		// ID token returned immediately if the response_type includes "id_token".
560		// Only valid for implicit and hybrid flows.
561		idToken       string
562		idTokenExpiry time.Time
563
564		accessToken = storage.NewID()
565	)
566
567	for _, responseType := range authReq.ResponseTypes {
568		switch responseType {
569		case responseTypeCode:
570			code = storage.AuthCode{
571				ID:            storage.NewID(),
572				ClientID:      authReq.ClientID,
573				ConnectorID:   authReq.ConnectorID,
574				Nonce:         authReq.Nonce,
575				Scopes:        authReq.Scopes,
576				Claims:        authReq.Claims,
577				Expiry:        s.now().Add(time.Minute * 30),
578				RedirectURI:   authReq.RedirectURI,
579				ConnectorData: authReq.ConnectorData,
580			}
581			if err := s.storage.CreateAuthCode(code); err != nil {
582				s.logger.Errorf("Failed to create auth code: %v", err)
583				s.renderError(w, http.StatusInternalServerError, "Internal server error.")
584				return
585			}
586
587			// Implicit and hybrid flows that try to use the OOB redirect URI are
588			// rejected earlier. If we got here we're using the code flow.
589			if authReq.RedirectURI == redirectURIOOB {
590				if err := s.templates.oob(w, code.ID); err != nil {
591					s.logger.Errorf("Server template error: %v", err)
592				}
593				return
594			}
595		case responseTypeToken:
596			implicitOrHybrid = true
597		case responseTypeIDToken:
598			implicitOrHybrid = true
599			var err error
600			idToken, idTokenExpiry, err = s.newIDToken(authReq.ClientID, authReq.Claims, authReq.Scopes, authReq.Nonce, accessToken, authReq.ConnectorID)
601			if err != nil {
602				s.logger.Errorf("failed to create ID token: %v", err)
603				s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
604				return
605			}
606		}
607	}
608
609	if implicitOrHybrid {
610		v := url.Values{}
611		v.Set("access_token", accessToken)
612		v.Set("token_type", "bearer")
613		v.Set("state", authReq.State)
614		if idToken != "" {
615			v.Set("id_token", idToken)
616			// The hybrid flow with only "code token" or "code id_token" doesn't return an
617			// "expires_in" value. If "code" wasn't provided, indicating the implicit flow,
618			// don't add it.
619			//
620			// https://openid.net/specs/openid-connect-core-1_0.html#HybridAuthResponse
621			if code.ID == "" {
622				v.Set("expires_in", strconv.Itoa(int(idTokenExpiry.Sub(s.now()).Seconds())))
623			}
624		}
625		if code.ID != "" {
626			v.Set("code", code.ID)
627		}
628
629		// Implicit and hybrid flows return their values as part of the fragment.
630		//
631		//   HTTP/1.1 303 See Other
632		//   Location: https://client.example.org/cb#
633		//     access_token=SlAV32hkKG
634		//     &token_type=bearer
635		//     &id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso
636		//     &expires_in=3600
637		//     &state=af0ifjsldkj
638		//
639		u.Fragment = v.Encode()
640	} else {
641		// The code flow add values to the URL query.
642		//
643		//   HTTP/1.1 303 See Other
644		//   Location: https://client.example.org/cb?
645		//     code=SplxlOBeZQQYbYS6WxSbIA
646		//     &state=af0ifjsldkj
647		//
648		q := u.Query()
649		q.Set("code", code.ID)
650		q.Set("state", authReq.State)
651		u.RawQuery = q.Encode()
652	}
653
654	http.Redirect(w, r, u.String(), http.StatusSeeOther)
655}
656
657func (s *Server) handleToken(w http.ResponseWriter, r *http.Request) {
658	clientID, clientSecret, ok := r.BasicAuth()
659	if ok {
660		var err error
661		if clientID, err = url.QueryUnescape(clientID); err != nil {
662			s.tokenErrHelper(w, errInvalidRequest, "client_id improperly encoded", http.StatusBadRequest)
663			return
664		}
665		if clientSecret, err = url.QueryUnescape(clientSecret); err != nil {
666			s.tokenErrHelper(w, errInvalidRequest, "client_secret improperly encoded", http.StatusBadRequest)
667			return
668		}
669	} else {
670		clientID = r.PostFormValue("client_id")
671		clientSecret = r.PostFormValue("client_secret")
672	}
673
674	client, err := s.storage.GetClient(clientID)
675	if err != nil {
676		if err != storage.ErrNotFound {
677			s.logger.Errorf("failed to get client: %v", err)
678			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
679		} else {
680			s.tokenErrHelper(w, errInvalidClient, "Invalid client credentials.", http.StatusUnauthorized)
681		}
682		return
683	}
684
685	if err := checkCost([]byte(client.Secret)); err != nil {
686		s.logger.Errorf("failed to check cost of client secret: %v", err)
687		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
688		return
689	}
690	if err := bcrypt.CompareHashAndPassword([]byte(client.Secret), []byte(clientSecret)); err != nil {
691		s.tokenErrHelper(w, errInvalidClient, "Invalid client credentials.", http.StatusUnauthorized)
692		return
693	}
694
695	grantType := r.PostFormValue("grant_type")
696	switch grantType {
697	case grantTypeAuthorizationCode:
698		s.handleAuthCode(w, r, client)
699	case grantTypeRefreshToken:
700		s.handleRefreshToken(w, r, client)
701	case grantTypeClientCredentials:
702		s.handleClientCredentialsGrant(w, r, client)
703	case grantTypePassword:
704		s.handlePasswordGrant(w, r, client)
705	default:
706		s.tokenErrHelper(w, errInvalidGrant, "", http.StatusBadRequest)
707	}
708}
709
710// handle an access token request https://tools.ietf.org/html/rfc6749#section-4.1.3
711func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client storage.Client) {
712	code := r.PostFormValue("code")
713	redirectURI := r.PostFormValue("redirect_uri")
714
715	authCode, err := s.storage.GetAuthCode(code)
716	if err != nil || s.now().After(authCode.Expiry) || authCode.ClientID != client.ID {
717		if err != storage.ErrNotFound {
718			s.logger.Errorf("failed to get auth code: %v", err)
719			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
720		} else {
721			s.tokenErrHelper(w, errInvalidRequest, "Invalid or expired code parameter.", http.StatusBadRequest)
722		}
723		return
724	}
725
726	if authCode.RedirectURI != redirectURI {
727		s.tokenErrHelper(w, errInvalidRequest, "redirect_uri did not match URI from initial request.", http.StatusBadRequest)
728		return
729	}
730
731	accessToken := storage.NewID()
732	idToken, expiry, err := s.newIDToken(client.ID, authCode.Claims, authCode.Scopes, authCode.Nonce, accessToken, authCode.ConnectorID)
733	if err != nil {
734		s.logger.Errorf("failed to create ID token: %v", err)
735		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
736		return
737	}
738
739	if err := s.storage.DeleteAuthCode(code); err != nil {
740		s.logger.Errorf("failed to delete auth code: %v", err)
741		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
742		return
743	}
744
745	reqRefresh := func() bool {
746		// Ensure the connector supports refresh tokens.
747		//
748		// Connectors like `saml` do not implement RefreshConnector.
749		conn, err := s.getConnector(authCode.ConnectorID)
750		if err != nil {
751			s.logger.Errorf("connector with ID %q not found: %v", authCode.ConnectorID, err)
752			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
753			return false
754		}
755
756		_, ok := conn.Connector.(connector.RefreshConnector)
757		if !ok {
758			return false
759		}
760
761		for _, scope := range authCode.Scopes {
762			if scope == scopeOfflineAccess {
763				return true
764			}
765		}
766		return false
767	}()
768	var refreshToken string
769	if reqRefresh {
770		refresh := storage.RefreshToken{
771			ID:            storage.NewID(),
772			Token:         storage.NewID(),
773			ClientID:      authCode.ClientID,
774			ConnectorID:   authCode.ConnectorID,
775			Scopes:        authCode.Scopes,
776			Claims:        authCode.Claims,
777			Nonce:         authCode.Nonce,
778			ConnectorData: authCode.ConnectorData,
779			CreatedAt:     s.now(),
780			LastUsed:      s.now(),
781		}
782		token := &internal.RefreshToken{
783			RefreshId: refresh.ID,
784			Token:     refresh.Token,
785		}
786		if refreshToken, err = internal.Marshal(token); err != nil {
787			s.logger.Errorf("failed to marshal refresh token: %v", err)
788			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
789			return
790		}
791
792		if err := s.storage.CreateRefresh(refresh); err != nil {
793			s.logger.Errorf("failed to create refresh token: %v", err)
794			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
795			return
796		}
797
798		// deleteToken determines if we need to delete the newly created refresh token
799		// due to a failure in updating/creating the OfflineSession object for the
800		// corresponding user.
801		var deleteToken bool
802		defer func() {
803			if deleteToken {
804				// Delete newly created refresh token from storage.
805				if err := s.storage.DeleteRefresh(refresh.ID); err != nil {
806					s.logger.Errorf("failed to delete refresh token: %v", err)
807					s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
808					return
809				}
810			}
811		}()
812
813		tokenRef := storage.RefreshTokenRef{
814			ID:        refresh.ID,
815			ClientID:  refresh.ClientID,
816			CreatedAt: refresh.CreatedAt,
817			LastUsed:  refresh.LastUsed,
818		}
819
820		// Try to retrieve an existing OfflineSession object for the corresponding user.
821		if session, err := s.storage.GetOfflineSessions(refresh.Claims.UserID, refresh.ConnectorID); err != nil {
822			if err != storage.ErrNotFound {
823				s.logger.Errorf("failed to get offline session: %v", err)
824				s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
825				deleteToken = true
826				return
827			}
828			offlineSessions := storage.OfflineSessions{
829				UserID:  refresh.Claims.UserID,
830				ConnID:  refresh.ConnectorID,
831				Refresh: make(map[string]*storage.RefreshTokenRef),
832			}
833			offlineSessions.Refresh[tokenRef.ClientID] = &tokenRef
834
835			// Create a new OfflineSession object for the user and add a reference object for
836			// the newly received refreshtoken.
837			if err := s.storage.CreateOfflineSessions(offlineSessions); err != nil {
838				s.logger.Errorf("failed to create offline session: %v", err)
839				s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
840				deleteToken = true
841				return
842			}
843		} else {
844			if oldTokenRef, ok := session.Refresh[tokenRef.ClientID]; ok {
845				// Delete old refresh token from storage.
846				if err := s.storage.DeleteRefresh(oldTokenRef.ID); err != nil {
847					s.logger.Errorf("failed to delete refresh token: %v", err)
848					s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
849					deleteToken = true
850					return
851				}
852			}
853
854			// Update existing OfflineSession obj with new RefreshTokenRef.
855			if err := s.storage.UpdateOfflineSessions(session.UserID, session.ConnID, func(old storage.OfflineSessions) (storage.OfflineSessions, error) {
856				old.Refresh[tokenRef.ClientID] = &tokenRef
857				return old, nil
858			}); err != nil {
859				s.logger.Errorf("failed to update offline session: %v", err)
860				s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
861				deleteToken = true
862				return
863			}
864
865		}
866	}
867	s.writeAccessToken(w, idToken, accessToken, refreshToken, expiry)
868}
869
870// handle a refresh token request https://tools.ietf.org/html/rfc6749#section-6
871func (s *Server) handleRefreshToken(w http.ResponseWriter, r *http.Request, client storage.Client) {
872	code := r.PostFormValue("refresh_token")
873	scope := r.PostFormValue("scope")
874	if code == "" {
875		s.tokenErrHelper(w, errInvalidRequest, "No refresh token in request.", http.StatusBadRequest)
876		return
877	}
878
879	token := new(internal.RefreshToken)
880	if err := internal.Unmarshal(code, token); err != nil {
881		// For backward compatibility, assume the refresh_token is a raw refresh token ID
882		// if it fails to decode.
883		//
884		// Because refresh_token values that aren't unmarshable were generated by servers
885		// that don't have a Token value, we'll still reject any attempts to claim a
886		// refresh_token twice.
887		token = &internal.RefreshToken{RefreshId: code, Token: ""}
888	}
889
890	refresh, err := s.storage.GetRefresh(token.RefreshId)
891	if err != nil {
892		s.logger.Errorf("failed to get refresh token: %v", err)
893		if err == storage.ErrNotFound {
894			s.tokenErrHelper(w, errInvalidRequest, "Refresh token is invalid or has already been claimed by another client.", http.StatusBadRequest)
895		} else {
896			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
897		}
898		return
899	}
900	if refresh.ClientID != client.ID {
901		s.logger.Errorf("client %s trying to claim token for client %s", client.ID, refresh.ClientID)
902		s.tokenErrHelper(w, errInvalidRequest, "Refresh token is invalid or has already been claimed by another client.", http.StatusBadRequest)
903		return
904	}
905	if refresh.Token != token.Token {
906		s.logger.Errorf("refresh token with id %s claimed twice", refresh.ID)
907		s.tokenErrHelper(w, errInvalidRequest, "Refresh token is invalid or has already been claimed by another client.", http.StatusBadRequest)
908		return
909	}
910
911	// Per the OAuth2 spec, if the client has omitted the scopes, default to the original
912	// authorized scopes.
913	//
914	// https://tools.ietf.org/html/rfc6749#section-6
915	scopes := refresh.Scopes
916	if scope != "" {
917		requestedScopes := strings.Fields(scope)
918		var unauthorizedScopes []string
919
920		for _, s := range requestedScopes {
921			contains := func() bool {
922				for _, scope := range refresh.Scopes {
923					if s == scope {
924						return true
925					}
926				}
927				return false
928			}()
929			if !contains {
930				unauthorizedScopes = append(unauthorizedScopes, s)
931			}
932		}
933
934		if len(unauthorizedScopes) > 0 {
935			msg := fmt.Sprintf("Requested scopes contain unauthorized scope(s): %q.", unauthorizedScopes)
936			s.tokenErrHelper(w, errInvalidRequest, msg, http.StatusBadRequest)
937			return
938		}
939		scopes = requestedScopes
940	}
941
942	conn, err := s.getConnector(refresh.ConnectorID)
943	if err != nil {
944		s.logger.Errorf("connector with ID %q not found: %v", refresh.ConnectorID, err)
945		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
946		return
947	}
948	ident := connector.Identity{
949		UserID:        refresh.Claims.UserID,
950		Name:          refresh.Claims.Name,
951		Username:      refresh.Claims.Username,
952		Email:         refresh.Claims.Email,
953		EmailVerified: refresh.Claims.EmailVerified,
954		Groups:        refresh.Claims.Groups,
955		ConnectorData: refresh.ConnectorData,
956	}
957
958	// Can the connector refresh the identity? If so, attempt to refresh the data
959	// in the connector.
960	//
961	// TODO(ericchiang): We may want a strict mode where connectors that don't implement
962	// this interface can't perform refreshing.
963	if refreshConn, ok := conn.Connector.(connector.RefreshConnector); ok {
964		newIdent, err := refreshConn.Refresh(r.Context(), parseScopes(scopes), ident)
965		if err != nil {
966			s.logger.Errorf("failed to refresh identity: %v", err)
967			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
968			return
969		}
970		ident = newIdent
971	}
972
973	claims := storage.Claims{
974		UserID:        ident.UserID,
975		Name:          ident.Name,
976		Username:      ident.Username,
977		Email:         ident.Email,
978		EmailVerified: ident.EmailVerified,
979		Groups:        ident.Groups,
980	}
981
982	accessToken := storage.NewID()
983	idToken, expiry, err := s.newIDToken(client.ID, claims, scopes, refresh.Nonce, accessToken, refresh.ConnectorID)
984	if err != nil {
985		s.logger.Errorf("failed to create ID token: %v", err)
986		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
987		return
988	}
989
990	newToken := &internal.RefreshToken{
991		RefreshId: refresh.ID,
992		Token:     storage.NewID(),
993	}
994	rawNewToken, err := internal.Marshal(newToken)
995	if err != nil {
996		s.logger.Errorf("failed to marshal refresh token: %v", err)
997		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
998		return
999	}
1000
1001	lastUsed := s.now()
1002	updater := func(old storage.RefreshToken) (storage.RefreshToken, error) {
1003		if old.Token != refresh.Token {
1004			return old, errors.New("refresh token claimed twice")
1005		}
1006		old.Token = newToken.Token
1007		// Update the claims of the refresh token.
1008		//
1009		// UserID intentionally ignored for now.
1010		old.Claims.Name = ident.Name
1011		old.Claims.Username = ident.Username
1012		old.Claims.Email = ident.Email
1013		old.Claims.EmailVerified = ident.EmailVerified
1014		old.Claims.Groups = ident.Groups
1015		old.ConnectorData = ident.ConnectorData
1016		old.LastUsed = lastUsed
1017		return old, nil
1018	}
1019
1020	// Update LastUsed time stamp in refresh token reference object
1021	// in offline session for the user.
1022	if err := s.storage.UpdateOfflineSessions(refresh.Claims.UserID, refresh.ConnectorID, func(old storage.OfflineSessions) (storage.OfflineSessions, error) {
1023		if old.Refresh[refresh.ClientID].ID != refresh.ID {
1024			return old, errors.New("refresh token invalid")
1025		}
1026		old.Refresh[refresh.ClientID].LastUsed = lastUsed
1027		return old, nil
1028	}); err != nil {
1029		s.logger.Errorf("failed to update offline session: %v", err)
1030		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1031		return
1032	}
1033
1034	// Update refresh token in the storage.
1035	if err := s.storage.UpdateRefreshToken(refresh.ID, updater); err != nil {
1036		s.logger.Errorf("failed to update refresh token: %v", err)
1037		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1038		return
1039	}
1040
1041	s.writeAccessToken(w, idToken, accessToken, rawNewToken, expiry)
1042}
1043
1044func (s *Server) handleClientCredentialsGrant(w http.ResponseWriter, r *http.Request, client storage.Client) {
1045
1046	if err := r.ParseForm(); err != nil {
1047		s.tokenErrHelper(w, errInvalidRequest, "Couldn't parse data", http.StatusBadRequest)
1048		return
1049	}
1050	q := r.Form
1051
1052	nonce := q.Get("nonce")
1053	scopes := strings.Fields(q.Get("scope"))
1054
1055	claims := storage.Claims{UserID: client.ID}
1056
1057	accessToken := storage.NewID()
1058	idToken, expiry, err := s.newIDToken(client.ID, claims, scopes, nonce, accessToken, "client")
1059	if err != nil {
1060		s.tokenErrHelper(w, errServerError, fmt.Sprintf("failed to create ID token: %v", err), http.StatusInternalServerError)
1061		return
1062	}
1063
1064	s.writeAccessToken(w, idToken, accessToken, "", expiry)
1065}
1066
1067func (s *Server) handlePasswordGrant(w http.ResponseWriter, r *http.Request, client storage.Client) {
1068
1069	// Parse the fields
1070	if err := r.ParseForm(); err != nil {
1071		s.tokenErrHelper(w, errInvalidRequest, "Couldn't parse data", http.StatusBadRequest)
1072		return
1073	}
1074	q := r.Form
1075
1076	nonce := q.Get("nonce")
1077	// Some clients, like the old go-oidc, provide extra whitespace. Tolerate this.
1078	scopes := strings.Fields(q.Get("scope"))
1079
1080	// Parse the scopes if they are passed
1081	var (
1082		unrecognized  []string
1083		invalidScopes []string
1084	)
1085	hasOpenIDScope := false
1086	for _, scope := range scopes {
1087		switch scope {
1088		case scopeOpenID:
1089			hasOpenIDScope = true
1090		case scopeOfflineAccess, scopeEmail, scopeProfile, scopeGroups, scopeFederatedID:
1091		default:
1092			peerID, ok := parseCrossClientScope(scope)
1093			if !ok {
1094				unrecognized = append(unrecognized, scope)
1095				continue
1096			}
1097
1098			isTrusted, err := s.validateCrossClientTrust(client.ID, peerID)
1099			if err != nil {
1100				s.tokenErrHelper(w, errInvalidClient, fmt.Sprintf("Error validating cross client trust %v.", err), http.StatusBadRequest)
1101				return
1102			}
1103			if !isTrusted {
1104				invalidScopes = append(invalidScopes, scope)
1105			}
1106		}
1107	}
1108	if !hasOpenIDScope {
1109		s.tokenErrHelper(w, errInvalidRequest, `Missing required scope(s) ["openid"].`, http.StatusBadRequest)
1110		return
1111	}
1112	if len(unrecognized) > 0 {
1113		s.tokenErrHelper(w, errInvalidRequest, fmt.Sprintf("Unrecognized scope(s) %q", unrecognized), http.StatusBadRequest)
1114		return
1115	}
1116	if len(invalidScopes) > 0 {
1117		s.tokenErrHelper(w, errInvalidRequest, fmt.Sprintf("Client can't request scope(s) %q", invalidScopes), http.StatusBadRequest)
1118		return
1119	}
1120
1121	// Which connector
1122	connID := s.passwordConnector
1123	conn, err := s.getConnector(connID)
1124	if err != nil {
1125		s.tokenErrHelper(w, errInvalidRequest, "Requested connector does not exist.", http.StatusBadRequest)
1126		return
1127	}
1128
1129	passwordConnector, ok := conn.Connector.(connector.PasswordConnector)
1130	if !ok {
1131		s.tokenErrHelper(w, errInvalidRequest, "Requested password connector does not correct type.", http.StatusBadRequest)
1132		return
1133	}
1134
1135	// Login
1136	username := q.Get("username")
1137	password := q.Get("password")
1138	identity, ok, err := passwordConnector.Login(r.Context(), parseScopes(scopes), username, password)
1139	if err != nil {
1140		s.tokenErrHelper(w, errInvalidRequest, "Could not login user", http.StatusBadRequest)
1141		return
1142	}
1143	if !ok {
1144		s.tokenErrHelper(w, errAccessDenied, "Invalid username or password", http.StatusUnauthorized)
1145		return
1146	}
1147
1148	// Build the claims to send the id token
1149	claims := storage.Claims{
1150		UserID:        identity.UserID,
1151		Username:      identity.Username,
1152		Name:          identity.Name,
1153		Email:         identity.Email,
1154		EmailVerified: identity.EmailVerified,
1155		Groups:        identity.Groups,
1156	}
1157
1158	accessToken := storage.NewID()
1159	idToken, expiry, err := s.newIDToken(client.ID, claims, scopes, nonce, accessToken, connID)
1160	if err != nil {
1161		s.tokenErrHelper(w, errServerError, fmt.Sprintf("failed to create ID token: %v", err), http.StatusInternalServerError)
1162		return
1163	}
1164
1165	reqRefresh := func() bool {
1166		// Ensure the connector supports refresh tokens.
1167		//
1168		// Connectors like `saml` do not implement RefreshConnector.
1169		_, ok := conn.Connector.(connector.RefreshConnector)
1170		if !ok {
1171			return false
1172		}
1173
1174		for _, scope := range scopes {
1175			if scope == scopeOfflineAccess {
1176				return true
1177			}
1178		}
1179		return false
1180	}()
1181	var refreshToken string
1182	if reqRefresh {
1183		refresh := storage.RefreshToken{
1184			ID:          storage.NewID(),
1185			Token:       storage.NewID(),
1186			ClientID:    client.ID,
1187			ConnectorID: connID,
1188			Scopes:      scopes,
1189			Claims:      claims,
1190			Nonce:       nonce,
1191			// ConnectorData: authCode.ConnectorData,
1192			CreatedAt: s.now(),
1193			LastUsed:  s.now(),
1194		}
1195		token := &internal.RefreshToken{
1196			RefreshId: refresh.ID,
1197			Token:     refresh.Token,
1198		}
1199		if refreshToken, err = internal.Marshal(token); err != nil {
1200			s.logger.Errorf("failed to marshal refresh token: %v", err)
1201			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1202			return
1203		}
1204
1205		if err := s.storage.CreateRefresh(refresh); err != nil {
1206			s.logger.Errorf("failed to create refresh token: %v", err)
1207			s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1208			return
1209		}
1210
1211		// deleteToken determines if we need to delete the newly created refresh token
1212		// due to a failure in updating/creating the OfflineSession object for the
1213		// corresponding user.
1214		var deleteToken bool
1215		defer func() {
1216			if deleteToken {
1217				// Delete newly created refresh token from storage.
1218				if err := s.storage.DeleteRefresh(refresh.ID); err != nil {
1219					s.logger.Errorf("failed to delete refresh token: %v", err)
1220					s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1221					return
1222				}
1223			}
1224		}()
1225
1226		tokenRef := storage.RefreshTokenRef{
1227			ID:        refresh.ID,
1228			ClientID:  refresh.ClientID,
1229			CreatedAt: refresh.CreatedAt,
1230			LastUsed:  refresh.LastUsed,
1231		}
1232
1233		// Try to retrieve an existing OfflineSession object for the corresponding user.
1234		if session, err := s.storage.GetOfflineSessions(refresh.Claims.UserID, refresh.ConnectorID); err != nil {
1235			if err != storage.ErrNotFound {
1236				s.logger.Errorf("failed to get offline session: %v", err)
1237				s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1238				deleteToken = true
1239				return
1240			}
1241			offlineSessions := storage.OfflineSessions{
1242				UserID:  refresh.Claims.UserID,
1243				ConnID:  refresh.ConnectorID,
1244				Refresh: make(map[string]*storage.RefreshTokenRef),
1245			}
1246			offlineSessions.Refresh[tokenRef.ClientID] = &tokenRef
1247
1248			// Create a new OfflineSession object for the user and add a reference object for
1249			// the newly received refreshtoken.
1250			if err := s.storage.CreateOfflineSessions(offlineSessions); err != nil {
1251				s.logger.Errorf("failed to create offline session: %v", err)
1252				s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1253				deleteToken = true
1254				return
1255			}
1256		} else {
1257			if oldTokenRef, ok := session.Refresh[tokenRef.ClientID]; ok {
1258				// Delete old refresh token from storage.
1259				if err := s.storage.DeleteRefresh(oldTokenRef.ID); err != nil {
1260					s.logger.Errorf("failed to delete refresh token: %v", err)
1261					s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1262					deleteToken = true
1263					return
1264				}
1265			}
1266
1267			// Update existing OfflineSession obj with new RefreshTokenRef.
1268			if err := s.storage.UpdateOfflineSessions(session.UserID, session.ConnID, func(old storage.OfflineSessions) (storage.OfflineSessions, error) {
1269				old.Refresh[tokenRef.ClientID] = &tokenRef
1270				return old, nil
1271			}); err != nil {
1272				s.logger.Errorf("failed to update offline session: %v", err)
1273				s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1274				deleteToken = true
1275				return
1276			}
1277
1278		}
1279	}
1280
1281	s.writeAccessToken(w, idToken, accessToken, refreshToken, expiry)
1282}
1283
1284func (s *Server) writeAccessToken(w http.ResponseWriter, idToken, accessToken, refreshToken string, expiry time.Time) {
1285	// TODO(ericchiang): figure out an access token story and support the user info
1286	// endpoint. For now use a random value so no one depends on the access_token
1287	// holding a specific structure.
1288	resp := struct {
1289		AccessToken  string `json:"access_token"`
1290		TokenType    string `json:"token_type"`
1291		ExpiresIn    int    `json:"expires_in"`
1292		RefreshToken string `json:"refresh_token,omitempty"`
1293		IDToken      string `json:"id_token"`
1294	}{
1295		accessToken,
1296		"bearer",
1297		int(expiry.Sub(s.now()).Seconds()),
1298		refreshToken,
1299		idToken,
1300	}
1301	data, err := json.Marshal(resp)
1302	if err != nil {
1303		s.logger.Errorf("failed to marshal access token response: %v", err)
1304		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
1305		return
1306	}
1307	w.Header().Set("Content-Type", "application/json")
1308	w.Header().Set("Content-Length", strconv.Itoa(len(data)))
1309	w.Write(data)
1310}
1311
1312func (s *Server) renderError(w http.ResponseWriter, status int, description string) {
1313	if err := s.templates.err(w, status, description); err != nil {
1314		s.logger.Errorf("Server template error: %v", err)
1315	}
1316}
1317
1318func (s *Server) tokenErrHelper(w http.ResponseWriter, typ string, description string, statusCode int) {
1319	if err := tokenErr(w, typ, description, statusCode); err != nil {
1320		s.logger.Errorf("token error response: %v", err)
1321	}
1322}
1323
1324// Check for username prompt override from connector. Defaults to "Username".
1325func usernamePrompt(conn connector.PasswordConnector) string {
1326	if attr := conn.Prompt(); attr != "" {
1327		return attr
1328	}
1329	return "Username"
1330}
1331