Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(202)

Unified Diff: server/auth/delegation/minter.go

Issue 2236163002: auth: Low-level API for minting delegation tokens. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@store-int-in-cache
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« server/auth/delegation/doc.go ('K') | « server/auth/delegation/doc.go ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: server/auth/delegation/minter.go
diff --git a/server/auth/delegation/minter.go b/server/auth/delegation/minter.go
new file mode 100644
index 0000000000000000000000000000000000000000..d0468f5f76c6fe78fed24b632aadc55f58220064
--- /dev/null
+++ b/server/auth/delegation/minter.go
@@ -0,0 +1,187 @@
+// Copyright 2016 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package delegation
+
+import (
+ "encoding/gob"
+ "fmt"
+ "time"
+
+ "golang.org/x/net/context"
+
+ "github.com/luci/luci-go/common/clock"
+ "github.com/luci/luci-go/server/auth/identity"
+ "github.com/luci/luci-go/server/auth/internal"
+)
+
+// TokenRequest describes parameters of a new delegation token.
+type TokenRequest struct {
+ // AuthServiceURL is root URL (e.g. https://<host>) of the service to use for
iannucci 2016/08/11 22:52:35 I think I almost asked this elsewhere, but deleted
Vadim Sh. 2016/08/11 23:25:37 Usually using https://<host> is less strings manip
+ // minting the delegation token.
+ //
+ // The token will be signed by the service's private key. Only services that
+ // trust this auth service would be able to accept the new token.
+ //
+ // Required.
+ AuthServiceURL string
+
+ // Audience is to whom caller's identity is delegated.
iannucci 2016/08/11 22:52:35 IIUC, the Audience set (all identities in this sli
Vadim Sh. 2016/08/11 23:25:37 Correct.
+ //
+ // Only clients that can prove they are intended audience (e.g. by presenting
+ // valid access token) would be able to use the delegation token.
+ //
+ // Optional. If both Audience and AudienceGroups are empty, the token is
+ // usable by any bearer.
iannucci 2016/08/11 22:52:35 that's a bit awkward; if you somehow strip both of
Vadim Sh. 2016/08/11 23:25:37 Done.
+ Audience []identity.Identity
+
+ // AudienceGroups can be used to specify a group (or a bunch of groups) as
+ // a target audience of the token.
+ //
+ // It works in addition to Audience.
+ //
+ // Optional. If both Audience and AudienceGroups are empty, the token is
+ // usable by any bearer.
+ AudienceGroups []string
+
+ // Services is a list of 'service:...' identities with services that accept
+ // the token.
+ //
+ // This can be used to limit the scope of the token.
+ //
+ // Optional. If empty, the token is accepted by any LUCI service.
+ Services []identity.Identity
iannucci 2016/08/11 22:52:35 Yeah I'm not super happy with go's zero-value for
Vadim Sh. 2016/08/11 23:25:37 Done.
+
+ // Impersonate defines on whose behalf the token is being created.
+ //
+ // By default the token delegates an identity of whoever requested it. This
+ // identity is extracted from credentials that accompany token minting request
+ // (e.g. OAuth access token).
iannucci 2016/08/11 22:52:35 could we ever run into a situation where this extr
Vadim Sh. 2016/08/11 23:25:37 No, auth_service enforces that user is not using d
+ //
+ // By using 'Impersonate' a caller can make a token that delegates someone
+ // else's identity, effectively producing an impersonation token.
+ //
+ // Only limited set of callers can do this. The set of who can be impersonated
+ // is also limited. The rules are enforced by the auth service.
+ Impersonate identity.Identity
+
+ // ValidityDuration defines for how long the token would be valid.
+ //
+ // Maximum theoretical TTL is limited by the lifetime of the signing key of
+ // the auth service (~= 24 hours). Minimum acceptable value is 30 sec.
+ //
+ // Required.
+ ValidityDuration time.Duration
+
+ // Intent is a reason why the token is created.
+ //
+ // Used only for logging purposes on the auth service, will be indexed. Should
+ // be a short identifier-like string.
iannucci 2016/08/11 22:52:35 Will it be publicly visible, or could you put priv
Vadim Sh. 2016/08/11 23:25:37 It is not encoded in the token body. It ends up in
+ //
+ // Optional.
+ Intent string
+}
+
+// Token is actual delegation token with its expiration time and ID.
+type Token struct {
+ Token string // base64-encoded URL-safe blob with the token
+ Expiry time.Time // UTC time when it expires (also encoded in Token)
+ SubtokenID string // identifier of the token (also encoded in Token)
+}
+
+// CreateToken makes a request to the auth service to generate the token.
+//
+// If uses current service's credentials to authenticate the request.
+//
+// If req.Impersonate is not used, the identity encoded in the authentication
+// credentials will be delegated by the token, otherwise the service will check
+// that caller is allowed to do the impersonation and will return a token that
+// delegates the identity specified by req.Impersonate.
+func CreateToken(c context.Context, req TokenRequest) (*Token, error) {
+ // See https://github.com/luci/luci-py/blob/master/appengine/auth_service/delegation.py.
+ var params struct {
+ Audience []string `json:"audience,omitempty"`
+ Services []string `json:"services,omitempty"`
+ ValidityDuration int `json:"validity_duration"`
+ Impersonate string `json:"impersonate,omitempty"`
+ Intent string `json:"intent,omitempty"`
+ }
+
+ // Audience.
+ params.Audience = make([]string, 0, len(req.Audience)+len(req.AudienceGroups))
+ for _, aud := range req.Audience {
+ if err := aud.Validate(); err != nil {
+ return nil, err
+ }
+ params.Audience = append(params.Audience, string(aud))
+ }
+ for _, group := range req.AudienceGroups {
+ params.Audience = append(params.Audience, "group:"+group)
+ }
+ if len(params.Audience) == 0 {
+ params.Audience = []string{"*"} // means "Any bearer"
+ }
+
+ // Services.
+ params.Services = make([]string, 0, len(req.Services))
+ for _, srv := range req.Services {
+ if err := srv.Validate(); err != nil {
+ return nil, err
+ }
+ params.Services = append(params.Services, string(srv))
+ }
+ if len(params.Services) == 0 {
+ params.Services = []string{"*"} // means "Any service"
+ }
+
+ // Validity duration.
+ params.ValidityDuration = int(req.ValidityDuration / time.Second)
+ if params.ValidityDuration < 30 {
+ return nil, fmt.Errorf("ValidityDuration must be >= 30 sec, got %s", req.ValidityDuration)
+ }
+ if params.ValidityDuration > 24*3600 {
+ return nil, fmt.Errorf("ValidityDuration must be <= 24h, got %s", req.ValidityDuration)
+ }
+
+ // The rest of the fields.
+ if req.Impersonate != "" {
+ if err := req.Impersonate.Validate(); err != nil {
+ return nil, err
+ }
+ params.Impersonate = string(req.Impersonate)
+ }
+ params.Intent = req.Intent
+
+ var response struct {
+ DelegationToken string `json:"delegation_token,omitempty"`
+ ValidityDuration int `json:"validity_duration,omitempty"`
+ SubtokenID string `json:"subtoken_id,omitempty"`
+ Text string `json:"text,omitempty"` // for error responses
+ }
+
+ httpReq := internal.Request{
+ Method: "POST",
+ URL: req.AuthServiceURL + "/auth_service/api/v1/delegation/token/create",
+ Scopes: []string{"https://www.googleapis.com/auth/userinfo.email"},
+ Body: &params,
+ Out: &response,
+ }
+ if err := httpReq.Do(c); err != nil {
+ if response.Text != "" {
+ err = fmt.Errorf("%s - %s", err, response.Text)
+ }
+ return nil, err
+ }
+
+ return &Token{
+ Token: response.DelegationToken,
+ Expiry: clock.Now(c).Add(time.Duration(response.ValidityDuration) * time.Second).UTC(),
+ SubtokenID: response.SubtokenID,
+ }, nil
+}
+
+func init() {
+ // For token cache.
+ gob.Register(Token{})
+}
« server/auth/delegation/doc.go ('K') | « server/auth/delegation/doc.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698