Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

google: no way to set jws.PrivateClaims (needed for Firebase) #198

Open
kenshaw opened this issue Aug 31, 2016 · 3 comments
Open

google: no way to set jws.PrivateClaims (needed for Firebase) #198

kenshaw opened this issue Aug 31, 2016 · 3 comments

Comments

@kenshaw
Copy link

kenshaw commented Aug 31, 2016

When using google.JWTConfigFromJSON, there is no way to set the underlying jws.PrivateClaims on the token. This is needed for use with Firebase when later using the related oauth2.Transport and oauth2.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:

bearerTS, _ := google.TokenSourceFromServiceAccountCredentialsJSON("service-account-credentials.json")

bearerTS.AddClaims(map[string]interface{}{"uid": "my user id"})

cl := &http.Client{
    Transport: &oauth2.Transport{ Source: bearerTS },
}

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!

@kenshaw
Copy link
Author

kenshaw commented Sep 3, 2016

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.

@rakyll
Copy link
Contributor

rakyll commented Sep 3, 2016

/cc @adg

@kenshaw
Copy link
Author

kenshaw commented Sep 20, 2016

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants