oauth2
oauth2 copied to clipboard
Help Needed for Adding CustomClaims(Role/Country etc) of User
Hi Team, I have created a basic setup for Oauth2(Password grant) using this library. Its working, but I need to add more custom claims. Currently only userid is added as below.
"UserID": "[email protected]",
"Role": "",
"Country": ""
}
My Question is do I need to make multiple DB queries if I needed to add these claims. Also I am not able to understand the entrypoint to this method. The last call I see is to Manager.GenerateAccessToken(gt, tgr)
Token(data *oauth2.GenerateBasic, isGenRefresh bool)
Thanks in advance
UserID is a string type, you can encode the data you need into a string using JSON encoding.
you means to say to use userID for all the claims
Yeah, that's definitely not the best way to do this.
I think there's some confusion here. When you're saying "claims", I guess you're referring to JWT which this library doesn't use by default. It does use and generate tokens, but not JWT. That's why you can't add 'claims' to the default token model, because it's not a JWT and there's no such thing as 'claims', just a replaceable AccessGenerate struct to generate the access_token.
So, how is the access token generated?
If you take a look at the code in the manager.go:GenerateAccessToken, the access_token is generated here
It instantiates the default AccessGenerate: generates/access.go In that file, the Token() method generates the access_token itself.
After generating the access_token, the token info is stored in the token store to (I believe, correct me if I'm wrong @LyricTian) later retrieve the token info from the store (in memory or persistent), rather than decoding the access_token and to store the token so you can e.g. invalidate it —and that kind of stuff— with the tokenStore (you can use the tokenStore methods for that).
How to create a custom access token?
Good news are that you CAN change the way the access token is generated, as the README states: you CAN use JWT to generate access tokens.
How? Easy. Just use a custom AccessGenerate struct like the one in generates/access.go . The library itself includes here: generates/jwt_access.go the JWTAccessGenerate you need to create custom access_token with JWT.
Change that file as you want with the claims you want:
func (a *JWTAccessGenerate) Token(data *oauth2.GenerateBasic, isGenRefresh bool) (access, refresh string, err error) {
claims := &JWTAccessClaims{
ClientID: data.Client.GetID(),
UserID: data.UserID,
ExpiredAt: data.TokenInfo.GetAccessCreateAt().Add(data.TokenInfo.GetAccessExpiresIn()).Unix(),
}
token := jwt.NewWithClaims(a.SignedMethod, claims)
var key interface{}
and then change the AccessGenerate used by default with manager.MapAccessGenerate():
import "gopkg.in/oauth2.v3/generates"
import "github.com/dgrijalva/jwt-go"
// ...
manager.MapAccessGenerate(generates.NewJWTAccessGenerate([]byte("00000000"), jwt.SigningMethodHS512))
If you want to parse it and retrieve the claims, use the JWT library (as in the README.md):
import "github.com/dgrijalva/jwt-go"
// Verify jwt access token
token, err := jwt.ParseWithClaims(access, &generates.JWTAccessClaims{}, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("parse error")
}
return []byte("00000000"), nil
})
if err != nil {
panic(err)
}
claims, ok := token.Claims.(*generates.JWTAccessClaims)
if !ok || !token.Valid {
panic("invalid token")
}
In my opinion, I would strongly recommend using a custom JWTAccessGenerate to generate your access_token, even if you don't need custom claims.
Just because then you could generate a JWT access_token with asymmetric encryption (like RSA), and you'd have a public and a private key in your auth server and only the public key in the Resource server. This way you can verify and decode tokens from the resource server without additional round trips to the auth server.
To conclude, I would want to thank you @LyricTian for this great library. It's well-coded and it's easy to customize, but as a suggestion I would pick someone who speaks native english (I don't) or open an issue with a 'help needed' label to look for someone, and get him to write up some documentation explaining this sort of things (eg. How is the access token generated, how to change the AccessGenerate, digging into the custom handlers —which I would rename to lifecycle hooks because at first I thought that by 'handlers' you were referring to something related with HTTP because 'handler' is like a convention in golang, also: isGenerateRefresh to allowRefresh.. but that would mean breaking changes so...—, etc.)
Anyway, I hope this helps anyone to add custom JWT claims.
I would like to thank @pmrt for his answer 👏.
Also I would like to ask why not use JWT standard claims. For example this mapping:
user_id
: "sub"
(https://tools.ietf.org/html/rfc7519#section-4.1.2)
expire_at
: "exp"
(https://tools.ietf.org/html/rfc7519#section-4.1.4)
client_id
: "aud"
(https://tools.ietf.org/html/rfc7519#section-4.1.3)
These are also specified here: https://github.com/dgrijalva/jwt-go/blob/06ea1031745cb8b3dab3f6a236daf2b0aa468b7e/claims.go#L18
I've created draft PR if I'm not clear :) ... https://github.com/go-oauth2/oauth2/pull/102
An example of adding public and/or private custom claims would be nice, as this is not obvious to me.
I would like to thank @pmrt for his answer 👏. Also I would like to ask why not use JWT standard claims. For example this mapping:
user_id
:"sub"
(https://tools.ietf.org/html/rfc7519#section-4.1.2)expire_at
:"exp"
(https://tools.ietf.org/html/rfc7519#section-4.1.4)client_id
:"aud"
(https://tools.ietf.org/html/rfc7519#section-4.1.3)These are also specified here: https://github.com/dgrijalva/jwt-go/blob/06ea1031745cb8b3dab3f6a236daf2b0aa468b7e/claims.go#L18
I've created draft PR if I'm not clear :) ... #102
Thanks for the PR. 👍
One question about the standard claims:
AFAIK, "aud"
in OAuth2 refers to the Resource Server that accepts this token, normally it would be the url of the resource. see discussion.
"sub"
would be relevant to the client_id
or user_id
depending on which OAuth2 Grant Type is involved (whether it is user-driven or machine-to-machine)
You are right, at the time of writing my comment I was under impression that aud should be used differently than its actual purpose.