Open kenshaw opened 8 years ago
For an example use case, I have written a oauth2util
package that provides a generic JWT Bearer Grant TokenSource. It is available here: https://github.com/knq/oauth2util
You can see this being used here: https://github.com/knq/firebase/blob/master/opts.go#L63
To further the discussion above, the oauth2util.JwtBearerToken
is a token source that uses the github.com/knq/jwt
package to encode generic JWTs at time of token redemption. As this is done dynamically, the set of claims can be modified with a call to AddClaim. In turn, JwtBearerToken
can be used with oauth2.Transport
.
/cc @adg
I realize this is slightly unconnected to this repository, but I wanted to update the ticket.
I have added two sub packages to the original github.com/knq/jwt
package, namely jwt/bearer
which provides a generic bearer grant assertion auth flow and jwt/gserviceaccount
that creates a compatible oauth2.TokenSource
that can be used with any Google API and uses the generic knq/jwt
. I have updated my knq/firebase
package to use this generic exchange. I will also be pushing some other examples for other Google APIs, and for some other services that use the generic bearer assertion.
The API I have settled on for adding claims to the bearer TokenSource
looks like the following (ignore the broken pseudo code):
tokenURL := "https://someplace.google.com/"
ctxt := context.Background()
signer, err := jwt.RS256.New(/* some kind of key data */)
ts, err := bearer.NewTokenSource(
signer, tokenURL, ctxt,
bearer.IssuedAt(true),
bearer.Claim("my_private_claim", "something"),
)
// add/modify claims after the fact
bearer.Claim("my_private_claim", "new value")(ts)
// the bearer token source is not reusable ...
ts = oauth2.ReuseTokenSource(nil, ts)
client := &http.Client{ Transport: &oauth2.Transport{ Source: ts } }
The jwt/gserviceaccount
package, I think provides a bit cleaner way to load Google service account credentials than the oauth2/google
package does:
gsa, err := gserviceaccount.FromFile("/path/to/credentials.json")
// wraps the bearer.NewTokenSource call
ts, err := gsa.TokenSource(subject, context, "scope1", "scope2")
// change the default expiration
bearer.ExpiresIn(10*time.Minute)(ts)
// add additional claims unique per google service:
bearer.Claim("name", "value")(ts)
// wrap as reusable
ts = oauth2.ReuseTokenSource(nil, ts)
You can see the actual, working example for Firebase here: https://github.com/knq/firebase/blob/master/opts.go#L91
I will publish examples of working with other Google APIs and some other services that use a bearer assertion grant will comment here again when they are available publicly.
I would be willing to do a formal proposal to add the jwt
and jwt/bearer
package as a top level golang.org/x/crypto
package if the authors here thought it was hitting the right notes. I would also suggest the gserviceaccount
package, but feel it should live elsewhere (no idea where).
If the above was adopted, then the oauth2/jwt
packages could be dropped completely, and it would take care of issues / confusion surrounding the fact that the oauth2/jwt
package is not a full JWT implementation.
When using
google.JWTConfigFromJSON
, there is no way to set the underlyingjws.PrivateClaims
on the token. This is needed for use with Firebase when later using the relatedoauth2.Transport
andoauth2.TokenSource
. For reference, Firebase populates a user id variable (named "uid" in the claims) for use with the Firebase security rules that allows impersonating a user. Additionally (although not directly related to this project), additional data is made available to the Firebase rules via the claims.I was going to submit a change that allowed setting
jws.PrivateClaims
based on the passed context, but after looking at the other open issues, there seems to be quite a number of outstanding issues with the jws/jwt packages, namely #189, #193, and #196. The issues seem to stem from jws/jwt implementation being fairly Google specific, and not really usable with anything other than Google service account credentials.I have a jwt package -- https://github.com/knq/jwt -- that provides a more generic way of encoding / decoding serialized JWTs. It handles arbitrary claim sets (so long as they can be json.Marshal'd), and I think adopting a similar API would provide (in my opinion) a more definitive way of dealing with JWTs.
Without getting into a compound discussion here (I don't mean to complicate issues here), it would be great if there was a golang.org/x/jwt package that could adequately deal with JWTs, and that the oauth2/google package used in turn.
I imagine that the API would look something like this:
I realize that the existing API has its own func's, but I hope that the above conveys the idea.
I'd be more than glad to submit a changeset doing something like the above, but wanted to open a discussion first before doing any "heavy lifting". As it is, I will be adding some kind of TokenSource() functionality to https://github.com/knq/jwt in order to "impersonate" users on Firebase.
Thanks for reading through the above!