rp: add VerifyTokens example

This commit is contained in:
Tim Möhlmann 2023-03-06 12:31:11 +02:00
parent 6e9d8739e8
commit 54eae928c9
4 changed files with 168 additions and 12 deletions

View file

@ -0,0 +1,58 @@
// Package gen allows generating of example tokens and claims.
//
// go run ./internal/testutil/gen
package main
import (
"encoding/json"
"fmt"
"os"
tu "github.com/zitadel/oidc/v2/internal/testutil"
"github.com/zitadel/oidc/v2/pkg/oidc"
)
var custom = map[string]any{
"foo": "Hello, World!",
"bar": struct {
Count int `json:"count,omitempty"`
Tags []string `json:"tags,omitempty"`
}{
Count: 22,
Tags: []string{"some", "tags"},
},
}
func main() {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
accessToken, atClaims := tu.NewAccessTokenCustom(
tu.ValidIssuer, tu.ValidSubject, tu.ValidAudience,
tu.ValidExpiration.AddDate(99, 0, 0), tu.ValidJWTID,
tu.ValidClientID, tu.ValidSkew, custom,
)
atHash, err := oidc.ClaimHash(accessToken, tu.SignatureAlgorithm)
if err != nil {
panic(err)
}
idToken, idClaims := tu.NewIDTokenCustom(
tu.ValidIssuer, tu.ValidSubject, tu.ValidAudience,
tu.ValidExpiration.AddDate(99, 0, 0), tu.ValidAuthTime,
tu.ValidNonce, tu.ValidACR, tu.ValidAMR, tu.ValidClientID,
tu.ValidSkew, atHash, custom,
)
fmt.Println("access token claims:")
if err := enc.Encode(atClaims); err != nil {
panic(err)
}
fmt.Printf("access token:\n%s\n", accessToken)
fmt.Println("ID token claims:")
if err := enc.Encode(idClaims); err != nil {
panic(err)
}
fmt.Printf("ID token:\n%s\n", idToken)
}

View file

@ -62,7 +62,7 @@ func TestVerifyTokens(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "wronf access token", name: "wrong access token",
accessToken: accessToken, accessToken: accessToken,
idTokenClaims: func() (string, *oidc.IDTokenClaims) { idTokenClaims: func() (string, *oidc.IDTokenClaims) {
return tu.NewIDToken( return tu.NewIDToken(

View file

@ -0,0 +1,85 @@
package rp_test
import (
"context"
"fmt"
tu "github.com/zitadel/oidc/v2/internal/testutil"
"github.com/zitadel/oidc/v2/pkg/client/rp"
"github.com/zitadel/oidc/v2/pkg/oidc"
)
// MyCustomClaims extends the TokenClaims base,
// so it implments the oidc.Claims interface.
// Instead of carying a map, we add needed fields
// to the struct for type safe access.
type MyCustomClaims struct {
oidc.TokenClaims
NotBefore oidc.Time `json:"nbf,omitempty"`
AccessTokenHash string `json:"at_hash,omitempty"`
Foo string `json:"foo,omitempty"`
Bar *Nested `json:"bar,omitempty"`
}
// GetAccessTokenHash is required to implement
// the oidc.IDClaims interface.
func (c *MyCustomClaims) GetAccessTokenHash() string {
return c.AccessTokenHash
}
// Nested struct types are also possible.
type Nested struct {
Count int `json:"count,omitempty"`
Tags []string `json:"tags,omitempty"`
}
/*
idToken caries the following claims. foo and bar are custom claims
{
"acr": "something",
"amr": [
"foo",
"bar"
],
"at_hash": "GKlH62ujLglHjxdM6ezzyQ",
"aud": [
"unit",
"test",
"555666"
],
"auth_time": 1678096954,
"azp": "555666",
"bar": {
"count": 22,
"tags": [
"some",
"tags"
]
},
"client_id": "555666",
"exp": 4802234675,
"foo": "Hello, World!",
"iat": 1678097014,
"iss": "local.com",
"jti": "9876",
"nbf": 1678097014,
"nonce": "12345",
"sub": "tim@local.com"
}
*/
const idToken = `eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOlsidW5pdCIsInRlc3QiXSwiYmFyIjoid29ybGQiLCJleHAiOjQ4MDIwMjU5MjAsImZvbyI6ImhlbGxvIiwiaWF0IjoxNjc3ODg4MjU5LCJpc3MiOiJsb2NhbC5jb20iLCJqdGkiOiI5ODc2IiwibmJmIjoxNjc3ODg4MjU5LCJzdWIiOiJ0aW1AbG9jYWwuY29tIn0.TbKRJnfyfn1PTC46VVXqqiKZl4gVmRPdQy8dxXvMtp1SAeMU4gSuu2qb-bNlVgFqQ5YqvveKH4mswcUf7DrqPx79roBEY1VZ6R0e10beZBg0UZ0XaBf9V9YJGTRQNEuETRjl6kMwVav4oyP8ZW74-AOrgSql7vxCX3FDRTRxt_7oeFRz2YzugFdHPOqQo4IHudQNMN9WD9b3QgoKDyj0BGxAQ9WpDE5N7WKIf6fXipSXJBQmf22QazXFZcUOGfKdhFYZ9eSlZQRDFJTguEtKwzk7wcxt6aJBsU-AEha2SucRXe3j7J56hAEsN5gC5i9edSdr8ebzrhnnLJ1t-PafhQ`
const accessToken = `eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOlsidW5pdCIsInRlc3QiXSwiYmFyIjp7ImNvdW50IjoyMiwidGFncyI6WyJzb21lIiwidGFncyJdfSwiZXhwIjo0ODAyMjM0Njc1LCJmb28iOiJIZWxsbywgV29ybGQhIiwiaWF0IjoxNjc4MDk3MDE0LCJpc3MiOiJsb2NhbC5jb20iLCJqdGkiOiI5ODc2IiwibmJmIjoxNjc4MDk3MDE0LCJzdWIiOiJ0aW1AbG9jYWwuY29tIn0.OUgk-B7OXjYlYFj-nogqSDJiQE19tPrbzqUHEAjcEiJkaWo6-IpGVfDiGKm-TxjXQsNScxpaY0Pg3XIh1xK6TgtfYtoLQm-5RYw_mXgb9xqZB2VgPs6nNEYFUDM513MOU0EBc0QMyqAEGzW-HiSPAb4ugCvkLtM1yo11Xyy6vksAdZNs_mJDT4X3vFXnr0jk0ugnAW6fTN3_voC0F_9HQUAkmd750OIxkAHxAMvEPQcpbLHenVvX_Q0QMrzClVrxehn5TVMfmkYYg7ocr876Bq9xQGPNHAcrwvVIJqdg5uMUA38L3HC2BEueG6furZGvc7-qDWAT1VR9liM5ieKpPg`
func ExampleVerifyTokens_customClaims() {
v := rp.NewIDTokenVerifier("local.com", "555666", tu.KeySet{})
// VerifyAccessToken can be called with the *MyCustomClaims.
claims, err := rp.VerifyTokens[*MyCustomClaims](context.TODO(), accessToken, idToken, v)
if err != nil {
panic(err)
}
// Here we have typesafe access to the custom claims
fmt.Println(claims.Foo, claims.Bar.Count, claims.Bar.Tags)
// Output: Hello, World! 22 [some tags]
}

View file

@ -21,7 +21,13 @@ type MyCustomClaims struct {
Scopes []string `json:"scope,omitempty"` Scopes []string `json:"scope,omitempty"`
AccessTokenUseNumber int `json:"at_use_nbr,omitempty"` AccessTokenUseNumber int `json:"at_use_nbr,omitempty"`
Foo string `json:"foo,omitempty"` Foo string `json:"foo,omitempty"`
Bar string `json:"bar,omitempty"` Bar *Nested `json:"bar,omitempty"`
}
// Nested struct types are also possible.
type Nested struct {
Count int `json:"count,omitempty"`
Tags []string `json:"tags,omitempty"`
} }
/* /*
@ -32,27 +38,34 @@ accessToken caries the following claims. foo and bar are custom claims
"unit", "unit",
"test" "test"
], ],
"bar": "world", "bar": {
"exp": 4802025920, "count": 22,
"foo": "hello", "tags": [
"iat": 1677888259, "some",
"tags"
]
},
"exp": 4802234675,
"foo": "Hello, World!",
"iat": 1678097014,
"iss": "local.com", "iss": "local.com",
"jti": "9876", "jti": "9876",
"nbf": 1677888259, "nbf": 1678097014,
"sub": "tim@local.com" "sub": "tim@local.com"
} }
*/ */
const accessToken = `eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOlsidW5pdCIsInRlc3QiXSwiYmFyIjoid29ybGQiLCJleHAiOjQ4MDIwMjU5MjAsImZvbyI6ImhlbGxvIiwiaWF0IjoxNjc3ODg4MjU5LCJpc3MiOiJsb2NhbC5jb20iLCJqdGkiOiI5ODc2IiwibmJmIjoxNjc3ODg4MjU5LCJzdWIiOiJ0aW1AbG9jYWwuY29tIn0.TbKRJnfyfn1PTC46VVXqqiKZl4gVmRPdQy8dxXvMtp1SAeMU4gSuu2qb-bNlVgFqQ5YqvveKH4mswcUf7DrqPx79roBEY1VZ6R0e10beZBg0UZ0XaBf9V9YJGTRQNEuETRjl6kMwVav4oyP8ZW74-AOrgSql7vxCX3FDRTRxt_7oeFRz2YzugFdHPOqQo4IHudQNMN9WD9b3QgoKDyj0BGxAQ9WpDE5N7WKIf6fXipSXJBQmf22QazXFZcUOGfKdhFYZ9eSlZQRDFJTguEtKwzk7wcxt6aJBsU-AEha2SucRXe3j7J56hAEsN5gC5i9edSdr8ebzrhnnLJ1t-PafhQ` const accessToken = `eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOlsidW5pdCIsInRlc3QiXSwiYmFyIjp7ImNvdW50IjoyMiwidGFncyI6WyJzb21lIiwidGFncyJdfSwiZXhwIjo0ODAyMjM0Njc1LCJmb28iOiJIZWxsbywgV29ybGQhIiwiaWF0IjoxNjc4MDk3MDE0LCJpc3MiOiJsb2NhbC5jb20iLCJqdGkiOiI5ODc2IiwibmJmIjoxNjc4MDk3MDE0LCJzdWIiOiJ0aW1AbG9jYWwuY29tIn0.OUgk-B7OXjYlYFj-nogqSDJiQE19tPrbzqUHEAjcEiJkaWo6-IpGVfDiGKm-TxjXQsNScxpaY0Pg3XIh1xK6TgtfYtoLQm-5RYw_mXgb9xqZB2VgPs6nNEYFUDM513MOU0EBc0QMyqAEGzW-HiSPAb4ugCvkLtM1yo11Xyy6vksAdZNs_mJDT4X3vFXnr0jk0ugnAW6fTN3_voC0F_9HQUAkmd750OIxkAHxAMvEPQcpbLHenVvX_Q0QMrzClVrxehn5TVMfmkYYg7ocr876Bq9xQGPNHAcrwvVIJqdg5uMUA38L3HC2BEueG6furZGvc7-qDWAT1VR9liM5ieKpPg`
func ExampleVerifyAccessToken_customClaims() { func ExampleVerifyAccessToken_customClaims() {
v := op.NewAccessTokenVerifier("local.com", tu.KeySet{}) v := op.NewAccessTokenVerifier("local.com", tu.KeySet{})
// Now VerifyAccessToken can be called with the *MyCustomClaims type to provide // VerifyAccessToken can be called with the *MyCustomClaims.
// type safe access to all the Claims.
claims, err := op.VerifyAccessToken[*MyCustomClaims](context.TODO(), accessToken, v) claims, err := op.VerifyAccessToken[*MyCustomClaims](context.TODO(), accessToken, v)
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Println(claims.Foo, claims.Bar)
// Output: hello world // Here we have typesafe access to the custom claims
fmt.Println(claims.Foo, claims.Bar.Count, claims.Bar.Tags)
// Output: Hello, World! 22 [some tags]
} }