When PostgREST receives a request that contains a valid JWT with a role claim it will switch to a database role with that name for the duration of the HTTP request. GoTrue doesn’t issue JWT’s with a role claim by default but it’s a pretty easy fix to make it do so.

To start, you’ll probably want to fork the GoTrue repo so you have a place to make your changes. Once you have a copy of their baseline you can start editing.

The first change that we need to make is to alter the structure that holds the user claims when GoTrue generates a new access token. Specifically, we need to add the Role claim to the GoTrueClaims structure in api/token.go like so:

type GoTrueClaims struct {
	jwt.StandardClaims
	Email        string                 `json:"email"`
	AppMetaData  map[string]interface{} `json:"app_metadata"`
	UserMetaData map[string]interface{} `json:"user_metadata"`
	Role         string                 `json:"role"`
}

The second change that we need to make is to populate the new Role claim in the generateAccessToken function in the same file. You have a choice here as to what you want to populate the claim with. Our team chose to populate it with the corresponding value from the GoTrue User structure.

func generateAccessToken(user *models.User, expiresIn time.Duration, secret string) (string, error) {
	claims := &GoTrueClaims{
		StandardClaims: jwt.StandardClaims{
			Subject:   user.ID.String(),
			Audience:  user.Aud,
			ExpiresAt: time.Now().Add(expiresIn).Unix(),
		},
		Email:        user.Email,
		AppMetaData:  user.AppMetaData,
		UserMetaData: user.UserMetaData,
		Role:         user.Role
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(secret))
}

That’s it. Your GoTrue instance should now issue JWT access tokens with a role claim that can be used by PostgREST.