"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
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)
func CheckIDToken(idToken string) error {
// fetch jwks_uri from Authgear
// you can cache the value of jwks to have better performance
set, err := FetchJWK(baseAddress)
return fmt.Errorf("failed to fetch JWK: %s", err)
token, err := jwt.ParseString(idToken, jwt.WithKeySet(set))
return fmt.Errorf("invalid token: %s", err)
err = jwt.Validate(token,
jwt.WithClock(jwt.ClockFunc(
func() time.Time { return time.Now().UTC() },
jwt.WithIssuer(baseAddress),
return fmt.Errorf("invalid token: %s", err)
authTimeAny, ok := token.Get("auth_time")
return fmt.Errorf("no auth_time")
authTimeUnix, ok := authTimeAny.(float64)
return fmt.Errorf("auth_time is not number")
authTime := time.Unix(int64(authTimeUnix), 0)
diff := now.Sub(authTime)
if diff > 5*time.Minute {
return fmt.Errorf("auth_time is not recent enough")