"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
authzHeaderRegexp = regexp.MustCompile("(?i)^Bearer (.*)quot;)
baseAddress = "https://<your_app_endpoint>"
type OIDCDiscoveryDocument struct {
JWKSURI string `json:"jwks_uri"`
func FetchOIDCDiscoveryDocument(endpoint string) (*OIDCDiscoveryDocument, error) {
resp, err := http.DefaultClient.Get(endpoint)
if resp.StatusCode != http.StatusOK {
"failed to fetch discovery document: unexpected status code: %d",
var document OIDCDiscoveryDocument
err = json.NewDecoder(resp.Body).Decode(&document)
func FetchJWK(baseAddress string) (jwk.Set, error) {
doc, err := FetchOIDCDiscoveryDocument(
baseAddress + "/.well-known/openid-configuration",
set, err := jwk.Fetch(context.Background(), doc.JWKSURI)
// DecodeUser parse request Authorization header and obtain user id and claims
func DecodeUser(r *http.Request) (string, map[string]interface{}, error) {
// fetch jwks_uri from Authgear
// you can cache the value of jwks to have better performance
set, err := FetchJWK(baseAddress)
return "", nil, fmt.Errorf("failed to fetch JWK: %s", err)
// get jwt token from Authorization header
authzHeader := r.Header.Get("Authorization")
match := authzHeaderRegexp.FindStringSubmatch(authzHeader)
return "", nil, fmt.Errorf("no token")
token, err := jwt.ParseString(match[1], jwt.WithKeySet(set))
return "", nil, fmt.Errorf("invalid token: %s", err)
err = jwt.Validate(token,
jwt.WithClock(jwt.ClockFunc(
func() time.Time { return time.Now().UTC() },
jwt.WithAudience(baseAddress),
return "", nil, fmt.Errorf("invalid token: %s", err)
return token.Subject(), token.PrivateClaims(), nil
func handler(w http.ResponseWriter, r *http.Request) {
userid, claims, err := DecodeUser(r)
claims["https://authgear.com/claims/user/is_verified"].(bool)
claims["https://authgear.com/claims/user/is_anonymous"].(bool)
// ... your handler logic