mirror of
https://github.com/therootcompany/golib.git
synced 2026-03-29 03:24:07 +00:00
119 lines
3.6 KiB
Go
119 lines
3.6 KiB
Go
// Copyright 2026 AJ ONeal <aj@therootcompany.com> (https://therootcompany.com)
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
//
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Example oidc-id-token demonstrates OIDC ID Token validation using
|
|
// jwt.StandardClaims, which carries the full set of OIDC profile, email,
|
|
// and phone fields alongside the core token claims.
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/therootcompany/golib/auth/jwt"
|
|
)
|
|
|
|
func main() {
|
|
// --- Setup: create a signer + verifier for demonstration ---
|
|
pk, err := jwt.NewPrivateKey()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
signer, err := jwt.NewSigner([]*jwt.PrivateKey{pk})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
verifier := signer.Verifier()
|
|
|
|
// Create an ID Token validator that checks iss, sub, aud, exp, iat,
|
|
// and auth_time (per OIDC Core §3.1.3.7).
|
|
validator := jwt.NewIDTokenValidator(
|
|
[]string{"https://accounts.example.com"}, // allowed issuers
|
|
[]string{"my-app-client-id"}, // allowed audiences
|
|
nil, // azp - no authorized-party restriction
|
|
0, // grace period (0 = default 2s)
|
|
)
|
|
|
|
// --- Mint an ID Token with OIDC profile and contact fields ---
|
|
claims := &jwt.StandardClaims{
|
|
TokenClaims: jwt.TokenClaims{
|
|
Iss: "https://accounts.example.com",
|
|
Sub: "user-42",
|
|
Aud: jwt.Listish{"my-app-client-id"},
|
|
Exp: time.Now().Add(time.Hour).Unix(),
|
|
IAt: time.Now().Unix(),
|
|
AuthTime: time.Now().Unix(),
|
|
Nonce: "abc123",
|
|
},
|
|
|
|
// OIDC profile fields
|
|
Name: "Jane Doe",
|
|
GivenName: "Jane",
|
|
FamilyName: "Doe",
|
|
PreferredUsername: "janedoe",
|
|
Picture: "https://example.com/janedoe/photo.jpg",
|
|
Locale: "en-US",
|
|
|
|
// Contact fields with NullBool for *_verified
|
|
Email: "jane@example.com",
|
|
EmailVerified: jwt.NullBool{Bool: true, Valid: true},
|
|
|
|
PhoneNumber: "+1-555-867-5309",
|
|
PhoneNumberVerified: jwt.NullBool{Bool: false, Valid: true},
|
|
}
|
|
|
|
token, err := signer.SignToString(claims)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("signed ID token:", token[:40]+"...")
|
|
|
|
// --- Decode, verify signature, unmarshal, validate ---
|
|
jws, err := jwt.Decode(token)
|
|
if err != nil {
|
|
log.Fatal("decode:", err)
|
|
}
|
|
|
|
if err := verifier.Verify(jws); err != nil {
|
|
log.Fatal("verify:", err)
|
|
}
|
|
|
|
var got jwt.StandardClaims
|
|
if err := jws.UnmarshalClaims(&got); err != nil {
|
|
log.Fatal("unmarshal:", err)
|
|
}
|
|
|
|
hdr := jws.GetHeader()
|
|
var errs []error
|
|
errs = hdr.IsAllowedTyp(errs, []string{"JWT"})
|
|
if err := validator.Validate(errs, &got, time.Now()); err != nil {
|
|
log.Fatal("validate:", err)
|
|
}
|
|
|
|
// --- Print the decoded OIDC claims ---
|
|
fmt.Println()
|
|
fmt.Println("=== ID Token Claims ===")
|
|
fmt.Println("iss: ", got.Iss)
|
|
fmt.Println("sub: ", got.Sub)
|
|
fmt.Println("aud: ", got.Aud)
|
|
fmt.Println("nonce: ", got.Nonce)
|
|
fmt.Println()
|
|
fmt.Println("name: ", got.Name)
|
|
fmt.Println("given_name: ", got.GivenName)
|
|
fmt.Println("family_name: ", got.FamilyName)
|
|
fmt.Println("preferred: ", got.PreferredUsername)
|
|
fmt.Println("picture: ", got.Picture)
|
|
fmt.Println("locale: ", got.Locale)
|
|
fmt.Println()
|
|
fmt.Println("email: ", got.Email)
|
|
fmt.Printf("email_verified: value=%v valid=%v\n", got.EmailVerified.Bool, got.EmailVerified.Valid)
|
|
fmt.Println("phone: ", got.PhoneNumber)
|
|
fmt.Printf("phone_verified: value=%v valid=%v\n", got.PhoneNumberVerified.Bool, got.PhoneNumberVerified.Valid)
|
|
}
|