Generate JWT Refresh Token with go-oauth2/oauth2 library - go

I am generating Oauth2 access tokens with the go library https://github.com/go-oauth2/oauth2 (v3) . I do this with the following go pseudocode:
jwtParams := generates.JWTAccessGenerate{
SignedKey: []byte(secretKey),
SignedMethod: jwt.SigningMethodHS512,
}
manage.Manager.MapAccessGenerate(&jwtParams)
req := oauth2.TokenGenerateRequest{
ClientID: clientId,
UserID: userId,
RedirectURI: redirectUri,
Code: authCode,
}
gt := oauth2.GrantType("authorization_code")
tokenInfo, _ := manage.Manager.GenerateAccessToken(gt, &req)
The result I get is a JWT access token but refresh token is not.
access=XXXX.YYYYY expires=5m0s <== JWT token - OK
refresh=YNFCZUFBWTUEXE5WJMD68W expires=12000h0m0s <== MY ISSUE - Not JWT
How do I get this library to generate a JWT refresh token?
Update 17-Jan-2020: After more research, I noted that many implementations don't bother with JWT representations for refresh tokens, so I may not need to as well. I would still like to know if it's feasible with this library, for future reference.

Store the refresh value, use that refresh to refresh the access token in future:
req := oauth2.TokenGenerateRequest{
ClientID: clientId,
UserID: userId,
RedirectURI: redirectUri,
Code: authCode,
}
req.Refresh = refresh
req.Scope = "owner"
rti, err := manager.RefreshAccessToken(req)
You can also call manager.LoadRefreshToken(accessToken) to load the RefreshToken from accessToken.

Related

Golang Oauth2 Service account returns empty refresh token string

I'm having a problem with the google/Oauth2 package when attempting to authenticate through the service account using a server to server authentication. Google responds with a token struct with an empty refresh token string, and the token expires in 1h, which I can't refresh as I don't have a refresh token.
Here is the code snippet I'm using:
/*
import(
"github.com/google/go-containerregistry/pkg/authn"
gcr "github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
*/
data, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", path, serviceAccountFilePath))
if err != nil {
log.Fatalf("Failed to read GCP service account key file: %s", err)
}
ctx := context.Background()
fmt.Println(scopes)
creds, err := google.CredentialsFromJSON(ctx, data, scopes...)
if err != nil {
log.Fatalf("Failed to load GCP service account credentials: %s", err)
}
t, _ := creds.TokenSource.Token()
fmt.Println(t.Expiry.Sub(time.Now()).String(), t.RefreshToken, ">>>")
r, err := gcr.NewRegistry("https://gcr.io")
if err != nil {
log.Fatalf("failed to ping registry: %s", err)
}
authToken := authn.FromConfig(authn.AuthConfig{
RegistryToken: t.AccessToken,
})
repo, err := gcr.NewRepository(fmt.Sprintf("%s/%s", urlPrefix, imageName))
repo.Registry = r
list, err := remote.List(repo, remote.WithAuth(authToken))
I tried different ways while using the service account for authentication, such as the config and JWT but I still got the same result.
Service accounts don't need / use refresh tokens.
Refresh tokens are used for offline access by standard Oauth2 authorization. If the user is offline then the application can use the refresh tokens to get an new access token and make requests on behalf of the user.
With service accounts they are already preauthorized and have access to the data they have. A request should return an access token once that access token expires after an hour you just make a new authorization request to get a new access token.
Refresh tokens are unnecessary in the case of service accounts. When the access token expires just run your auth code again to get a new one. Its saving you a step.
Thanks to #DalmTo's hints, I solved the problem.
So the fix for the such problem was by not using the credentials out of google.CredentialsFromJSON() func will return the token source without refreshing the token in case of passing the service account to that function, which means that you can't refresh your token when it expires again later. Also, anticipating and re-authenticating to generate a new token didn't work for me (no clue why).
So I had to convert the JSON of the service account into JWT through this func instead
scopes := []string{"https://www.googleapis.com/auth/cloud-platform"}
tokenSource, err := google.JWTAccessTokenSourceWithScope(serviceAccountFileBytes, scopes...)
The reason that this one works, is because it creates the JWT token internally through the service_account's properties such as client email and client_id and private_key as GCP allows us to create our local JWT tokens and encode them.

Possible to specify a proxy when refreshing an OAuth 2.0 Authorization Code flow token?

I have an application that is using the OAuth 2.0 Authorization Code flow and as part of that we regularly check whether a token has expired and refresh if it has. This is all done using golang's built in oauth2 library. The code that performs the refresh looks something like this:
if time.Now().Before(currentToken.Expiry) {
return false, nil
}
// if the expiry has passed, generate a new token
refreshConf := oauth2.Config{
ClientID: config.OAuthConfig.ClientID,
ClientSecret: config.OAuthConfig.ClientSecret,
Endpoint: oauth2.Endpoint{
TokenURL: config.TokenURL,
},
}
tokenRefreshSource := refreshConf.TokenSource(ctx, &currentToken.Token)
newToken, err := tokenRefreshSource.Token()
if err != nil {
return false, err
}
currentToken.Token = *newToken
Recently my work has cracked down on unregulated outbound traffic in an effort to make things more secure. Part of these changes has required that all outbound traffic go through a specific proxy. I've been able to do this by setting http.Client.Transport.Proxy for the requests that I'm manually sending, but I have not found a way to have the underlying request that is fired from tokenRefreshSource.Token() use a proxy.
Does anyone know if this is possible? If so - suggestions on how I can do it?

How to implement authorization using Keycloak

I created a REST API in Go that is necessary an authorization layer, for this layer I am trying use Keycloak. The API will be consumed by a third-party backend service, anyone knows the workflow to integrate Go client and keycloak or already implemented it? I figured out an adapter called Gocloak but in its documentation there is not any example for this purpose.
Authorization is typically application specific, so I can't help much there, but here's some information on authenticating JWTs from Keycloak. After JWTs are authenticated, you can use their claims to authorize the request.
Keycloak exposes what's known as a JSON Web Key Set (JWKS). This resource should be used to authenticate JWTs. I've wrote a package for this purpose. It's an extension of github.com/golang-jwt/jwt/v4.
The package is called github.com/MicahParks/keyfunc. I've pasted the code example for Keycloak below.
package main
import (
"log"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/MicahParks/keyfunc"
)
func main() {
// Get the JWKS URL.
//
// This is a local Keycloak JWKS endpoint for the master realm.
jwksURL := "http://localhost:8080/auth/realms/master/protocol/openid-connect/certs"
// Create the keyfunc options. Use an error handler that logs. Refresh the JWKS when a JWT signed by an unknown KID
// is found or at the specified interval. Rate limit these refreshes. Timeout the initial JWKS refresh request after
// 10 seconds. This timeout is also used to create the initial context.Context for keyfunc.Get.
options := keyfunc.Options{
RefreshErrorHandler: func(err error) {
log.Printf("There was an error with the jwt.Keyfunc\nError: %s", err.Error())
},
RefreshInterval: time.Hour,
RefreshRateLimit: time.Minute * 5,
RefreshTimeout: time.Second * 10,
RefreshUnknownKID: true,
}
// Create the JWKS from the resource at the given URL.
jwks, err := keyfunc.Get(jwksURL, options)
if err != nil {
log.Fatalf("Failed to create JWKS from resource at the given URL.\nError: %s", err.Error())
}
// Get a JWT to parse.
jwtB64 := "eyJhbGciOiJQUzM4NCIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMeDFGbWF5UDJZQnR4YXFTMVNLSlJKR2lYUktudzJvdjVXbVlJTUctQkxFIn0.eyJleHAiOjE2MTU0MDY5ODIsImlhdCI6MTYxNTQwNjkyMiwianRpIjoiMGY2NGJjYTktYjU4OC00MWFhLWFkNDEtMmFmZDM2OGRmNTFkIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.Rxrq41AxbWKIQHWv-Tkb7rqwel3sKT_R_AGvn9mPIHqhw1m7nsQWcL9t2a_8MI2hCwgWtYdgTF1xxBNmb2IW3CZkML5nGfcRrFvNaBHd3UQEqbFKZgnIX29h5VoxekyiwFaGD-0RXL83jF7k39hytEzTatwoVjZ-frga0KFl-nLce3OwncRXVCGmxoFzUsyu9TQFS2Mm_p0AMX1y1MAX1JmLC3WFhH3BohhRqpzBtjSfs_f46nE1-HKjqZ1ERrAc2fmiVJjmG7sT702JRuuzrgUpHlMy2juBG4DkVcMlj4neJUmCD1vZyZBRggfaIxNkwUhHtmS2Cp9tOcwNu47tSg"
// Parse the JWT.
token, err := jwt.Parse(jwtB64, jwks.Keyfunc)
if err != nil {
log.Fatalf("Failed to parse the JWT.\nError: %s", err.Error())
}
// Check if the token is valid.
if !token.Valid {
log.Fatalf("The token is not valid.")
}
log.Println("The token is valid.")
// End the background refresh goroutine when it's no longer needed.
jwks.EndBackground()
}

illegal base64 data at input byte 0 in go

I am starting with go and jwt.
For testing purpose I have a hardcoded secret.
And a route to get the key
const secretKey = "YOLOSWAG"
var mySigningKey = []byte(secretKey)
var GetTokenHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = jwt.MapClaims{
"admin": true,
"name": "John Doe",
"exp": time.Now().Add(time.Hour * 24).Unix(),
}
tokenString, _ := token.SignedString(mySigningKey)
w.Write([]byte(tokenString))
})
var jwtMiddleware = jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
return mySigningKey, nil
},
SigningMethod: jwt.SigningMethodHS256,
})
and later added the jwtMiddleware to my route
r.Handle("/protected", jwtMiddleware.Handler(ProtectedTestHandler)).Methods("GET")
So localhost:3000/protected will output an error Required authorization token not found
this works.
/token will output my token. This works too.
And finally /protected with (in postman) Authorization: Bearer {token}
Will output illegal base64 data at input byte 0
I am really confused why this happens.
Don't use curlies around your token. The documentation in many places is confusing because it wraps your token in curlies. It's meant to represent a placeholder. You're not actually supposed to wrap your token with them. Do NOT do it like this.
Bearer {my-special-token}
It should be done like this
Bearer my-special-token
I'm an absolute newb with GO at the moment as I'm learning this right now but I ran into this same issue and realized that the code I was using to pull the JWT Token out of the Authorization header was leaving a blank space as the first character of the JWT token string. This was presumably causing the string not be base64 decoded.
This was the offending code which was leaving a blank space in front of the. JWT token:
This removed the first 6 chars instead of 5 from the Authorization header to correct the problem.
I ran into this problem when following the tutorial here: https://medium.com/wesionary-team/jwt-authentication-in-golang-with-gin-63dbc0816d55
Repo: https://github.com/Bikash888/jwt-auth

Golang Oauth2 Get Token Scope

When using Golang's Oauth2 library:
https://godoc.org/golang.org/x/oauth2#Token
I exchange the authorisation code for access token and I get back this struct:
type Token struct {
// AccessToken is the token that authorizes and authenticates
// the requests.
AccessToken string `json:"access_token"`
// TokenType is the type of token.
// The Type method returns either this or "Bearer", the default.
TokenType string `json:"token_type,omitempty"`
// RefreshToken is a token that's used by the application
// (as opposed to the user) to refresh the access token
// if it expires.
RefreshToken string `json:"refresh_token,omitempty"`
// Expiry is the optional expiration time of the access token.
//
// If zero, TokenSource implementations will reuse the same
// token forever and RefreshToken or equivalent
// mechanisms for that TokenSource will not be used.
Expiry time.Time `json:"expiry,omitempty"`
// contains filtered or unexported fields
}
Now when I am using this access token in my application, I need to know the scope for which the token was granted.
But I don't see any property or method to get the scope?
How to get the token's scope so I can limit user's permissions based on it?
I can see that the Config struct has Scopes slice:
type Config struct {
// ClientID is the application's ID.
ClientID string
// ClientSecret is the application's secret.
ClientSecret string
// Endpoint contains the resource server's token endpoint
// URLs. These are constants specific to each server and are
// often available via site-specific packages, such as
// google.Endpoint or github.Endpoint.
Endpoint Endpoint
// RedirectURL is the URL to redirect users going through
// the OAuth flow, after the resource owner's URLs.
RedirectURL string
// Scope specifies optional requested permissions.
Scopes []string
}
It seems to me there is no way to get scope from a token though?
Surely the point of scope is that it should be part of the access token in order to validate permissions?
See the spec: https://www.rfc-editor.org/rfc/rfc6749#page-23
this should do the trick
func GetTokensScope(tokUrl string, clientId string, secret string) (string,error){
body := bytes.NewBuffer([]byte("grant_type=client_credentials&client_id="+clientId+"&client_secret="+secret+"&response_type=token"))
req, err := http.NewRequest("POST",tokUrl,body)
req.Header.Set("Content-Type","application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "",err
}
defer resp.Body.Close()
rsBody, err := ioutil.ReadAll(resp.Body)
type WithScope struct {
Scope string `json:"scope"`
}
var dat WithScope
err = json.Unmarshal(rsBody,&dat)
if err != nil {
return "",err
}
return dat.Scope,err
}

Resources