feat(example): add example server
This commit is contained in:
parent
c529b415bc
commit
70b0af6796
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"crv": "P-256",
|
||||||
|
"kid": "tsb1m6h3xjp1HjXmJY_8pTtHxld5UvWxoPG9b2e_0aY",
|
||||||
|
"kty": "EC",
|
||||||
|
"use": "sig",
|
||||||
|
"x": "VSQ5P-nwzOMVYowySPF8-FFRTLGXfY611ErayGgM4cc",
|
||||||
|
"y": "qSVv69YEJdM7waa_w8wegsaFDZaFZNkRNV-2PUGXb_E"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"e": "AQAB",
|
||||||
|
"kid": "dS08lAcJu_zmqIFkBwbI7rgi4OGlaF3uugjs6NysFEY",
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "q0yq8t-8Sw9nAJQAbDhiUMtxD_OEHigOekZrcLR38JkagqUZlxYZNp1B7NXM8GTymtz3qKzxUoI-mmE9gHq2nyDN8Jc_DTe_jnNFPD_bAxo92Ii_jpT74_6PR7I92BBvw0-ecxKHScJlO2tD2l1hxyOwpJ52Gt3WuXp2Ezsd3_14boTU4Z3Wh7WFNStz-BBwl09KR8UmVz1_pifJMnDEDXsRMEorFEbSDlJoZLAQgjAEwEZdmecH256WANKGylk1m5PWIBA59FMNXdQZIN1e6Cc0knaqZJHLor1hzmfSjyxxhSck0xk0HccUFNskS9QMoX05IvupxcnMBVPXIQBstw",
|
||||||
|
"use": "sig"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"issuer": "https://therootcompany.github.io/libauth/",
|
||||||
|
"jwks_uri": "https://therootcompany.github.io/libauth/.well-known/jwks.json"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
openid-configuration
|
47
README.md
47
README.md
|
@ -1,6 +1,7 @@
|
||||||
# [libauth](https://git.rootprojects.org/root/libauth)
|
# [libauth](https://git.rootprojects.org/root/libauth)
|
||||||
|
|
||||||
LibAuth for Go - A modern authentication framework that feels as light as a library.
|
LibAuth for Go - A modern authentication framework that feels as light as a
|
||||||
|
library.
|
||||||
|
|
||||||
[![godoc_button]][godoc]
|
[![godoc_button]][godoc]
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
whitelist, err := keyfetch.NewWhitelist([]string{"https://accounts.google.com"})
|
whitelist, err := keyfetch.NewWhitelist([]string{"https://therootcompany.github.io/libauth/"})
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -53,11 +54,45 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
How to create a demo token with [keypairs][https://webinstall.dev/keypairs]:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
my_key='./examples/privkey.ec.jwk.json'
|
||||||
|
my_claims='{
|
||||||
|
"iss": "https://therootcompany.github.io/libauth/",
|
||||||
|
"sub": "1",
|
||||||
|
"email_verified": false,
|
||||||
|
"email": "jo@example.com"
|
||||||
|
}'
|
||||||
|
|
||||||
|
keypairs sign \
|
||||||
|
--exp 1h \
|
||||||
|
"${my_key}" \
|
||||||
|
"${my_claims}" \
|
||||||
|
> jwt.txt
|
||||||
|
2> jws.json
|
||||||
|
```
|
||||||
|
|
||||||
How to pass an auth token:
|
How to pass an auth token:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://localhost:3000/api/users/profile \
|
pushd ./examples
|
||||||
-H 'Authorization: Bearer <xxxx.yyyy.zzzz>' \
|
go run ./server.go
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
--raw-data '{ "foo": "bar" }'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
my_token="$(cat ./examples/jwt.txt)"
|
||||||
|
|
||||||
|
curl -X POST http://localhost:3000/api/users/profile \
|
||||||
|
-H "Authorization: Bearer ${my_token}" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
--data-binary '{ "foo": "bar" }'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example OIDC Discovery URLs
|
||||||
|
|
||||||
|
- Demo:
|
||||||
|
<https://therootcompany.github.io/libauth/.well-known/openid-configuration>
|
||||||
|
- Auth0: <https://example.auth0.com/.well-known/openid-configuration>
|
||||||
|
- Okta: <https://example.okta.com/.well-known/openid-configuration>
|
||||||
|
- Google: <https://accounts.google.com/.well-known/openid-configuration>
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
These example RSA and ECDSA private keys can be validated against the demo site:
|
||||||
|
|
||||||
|
| Issuer | <https://therootcompany.github.io/libauth/> |
|
||||||
|
| :------------ | :-------------------------------------------------------------------------- |
|
||||||
|
| Discovery URL | <https://therootcompany.github.io/libauth/.well-known/openid-configuration> |
|
||||||
|
| JWKs URL | <https://therootcompany.github.io/libauth/.well-known/jwks.json> |
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
keypairs sign \
|
||||||
|
--exp 87660h \
|
||||||
|
./examples/privkey.ec.jwk.json \
|
||||||
|
'{
|
||||||
|
"iss": "https://therootcompany.github.io/libauth/",
|
||||||
|
"sub": "1",
|
||||||
|
"email_verified": false,
|
||||||
|
"email": "jo@example.com"
|
||||||
|
}' \
|
||||||
|
> ./examples/jwt.txt \
|
||||||
|
2> ./examples/jws.json
|
|
@ -0,0 +1,9 @@
|
||||||
|
module git.rootprojects.org/root/libauth/examples
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.rootprojects.org/root/keypairs v0.6.5
|
||||||
|
git.rootprojects.org/root/libauth v0.1.0
|
||||||
|
github.com/go-chi/chi/v5 v5.0.7
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
git.rootprojects.org/root/keypairs v0.6.5 h1:sdRAQD/O/JBS8+ZxUewXnY+cjQVDNH3TmcS+KtANZqA=
|
||||||
|
git.rootprojects.org/root/keypairs v0.6.5/go.mod h1:WGI8PadOp+4LjUuI+wNlSwcJwFtY8L9XuNjuO3213HA=
|
||||||
|
git.rootprojects.org/root/libauth v0.1.0 h1:qM73YYBLByoFTJUXH2ZeUhJdLzY35t4jgNoUAyqH2QA=
|
||||||
|
git.rootprojects.org/root/libauth v0.1.0/go.mod h1:bbLDWn0w7I1VfOMP2DZU/t/H9Ln0mT61K+ELH4ievVM=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"claims": {
|
||||||
|
"email": "jo@example.com",
|
||||||
|
"email_verified": false,
|
||||||
|
"exp": 1967702403,
|
||||||
|
"iss": "https://therootcompany.github.io/libauth/",
|
||||||
|
"sub": "1"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"alg": "ES256",
|
||||||
|
"kid": "tsb1m6h3xjp1HjXmJY_8pTtHxld5UvWxoPG9b2e_0aY",
|
||||||
|
"typ": "JWT"
|
||||||
|
},
|
||||||
|
"payload": "eyJlbWFpbCI6ImpvQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJleHAiOjE5Njc3MDI0MDMsImlzcyI6Imh0dHBzOi8vdGhlcm9vdGNvbXBhbnkuZ2l0aHViLmlvL2xpYmF1dGgvIiwic3ViIjoiMSJ9",
|
||||||
|
"protected": "eyJhbGciOiJFUzI1NiIsImtpZCI6InRzYjFtNmgzeGpwMUhqWG1KWV84cFR0SHhsZDVVdld4b1BHOWIyZV8wYVkiLCJ0eXAiOiJKV1QifQ",
|
||||||
|
"signature": "-vezm5OL5c4vlFvvj0Z4HAbX2nAAabO_37w5wMtnD2_OuzTDhM_4wRxzEZ5sdUIJ0rM7gGAv7B3CfGSibr0TJA"
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
eyJhbGciOiJFUzI1NiIsImtpZCI6InRzYjFtNmgzeGpwMUhqWG1KWV84cFR0SHhsZDVVdld4b1BHOWIyZV8wYVkiLCJ0eXAiOiJKV1QifQ.eyJlbWFpbCI6ImpvQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJleHAiOjE5Njc3MDI0MDMsImlzcyI6Imh0dHBzOi8vdGhlcm9vdGNvbXBhbnkuZ2l0aHViLmlvL2xpYmF1dGgvIiwic3ViIjoiMSJ9.-vezm5OL5c4vlFvvj0Z4HAbX2nAAabO_37w5wMtnD2_OuzTDhM_4wRxzEZ5sdUIJ0rM7gGAv7B3CfGSibr0TJA
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"crv": "P-256",
|
||||||
|
"d": "xcwk5FI9QuCK7Ap-aRsjdaHQx6ckoXcgQQ_HQbarUWE",
|
||||||
|
"kty": "EC",
|
||||||
|
"x": "VSQ5P-nwzOMVYowySPF8-FFRTLGXfY611ErayGgM4cc",
|
||||||
|
"y": "qSVv69YEJdM7waa_w8wegsaFDZaFZNkRNV-2PUGXb_E"
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"d": "d3iFUdcRcBhR8mlG0jOQ_mClfkaMwquVTVqH3JdBf6CIiM21R1a2Rwzuyctjn9YIDlJGuHHF7ZHBL9LaHh13-QvcFgymgQV8qFFk3Fx813EZ6UeWsk7eT2lfbNW3pFXyXPnOvNsTWDIogISTUl0GsOkHbgjGvn4yIDJ033y_nVP6MybnTS7Z-bNZj7YUo9srlTUxXLHd8U9r8b5UIWRCyWeOhsxNjiCCOEinmL6dISjKeubpoG6mbBxC7ptglINqVYHu9HxFfOqd-epbQw0Y5DGIvd6iyzbsfmx8DJwXoJ31oyiFMPdgO5kbZbwEDBCxnHBzxH8M3kfYmL5uYRfnYQ",
|
||||||
|
"dp": "YAJfR37hBBwaXct4qkY7_pM2hHRtUOwEAq6QiSB24ogWzq8ehPfIAE_Vu1NAPhMVZk_UPt9A8444pUDLGXpe1-Y66TSC9l6x0g4LhX44A0sc6Wh5Cpbjjr77aim5aVf1AyR-SiPkHNY4SGl_VbkBtk6JOTlww5QwDEdbvcl8ugs",
|
||||||
|
"dq": "qO8KbV5svwEAdbHiKt0iFts53PQiD1p-XkvAeV4TPJKYfEmkQPqnpfe6pjN-dnl0S_OwdajLvnU_qVXs0Gec8hJHBf6nBuU0DpIxUML1R-aBqnEPH6-tH8pYK-fD0qr20Qr8tkR8hKI5cthOS0wiqh9A7FC4AUZxLgWOFhs0VdE",
|
||||||
|
"e": "AQAB",
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "q0yq8t-8Sw9nAJQAbDhiUMtxD_OEHigOekZrcLR38JkagqUZlxYZNp1B7NXM8GTymtz3qKzxUoI-mmE9gHq2nyDN8Jc_DTe_jnNFPD_bAxo92Ii_jpT74_6PR7I92BBvw0-ecxKHScJlO2tD2l1hxyOwpJ52Gt3WuXp2Ezsd3_14boTU4Z3Wh7WFNStz-BBwl09KR8UmVz1_pifJMnDEDXsRMEorFEbSDlJoZLAQgjAEwEZdmecH256WANKGylk1m5PWIBA59FMNXdQZIN1e6Cc0knaqZJHLor1hzmfSjyxxhSck0xk0HccUFNskS9QMoX05IvupxcnMBVPXIQBstw",
|
||||||
|
"p": "wzVny5EpwoVF_ljD7mxVVk0gjWTfJ2lkXunb5HbpB_1XkY464kptA9WzSa_0kuSegUrcAPL3KrLPx2ZOT9y9Q5q2RLH4MSCm2uarRvYTiKt9LkIdPFH68iUrrt8wAMX4KXA13mwD-hPpdxNB_Dz1qWqaodW_X-zXLEntBv2tUXs",
|
||||||
|
"q": "4KUoRNx164BsU8mFRV9fs4ZUm2fR-Z_JxlUj04dOcu4YYxQNmiSSXDUcTxx3-s4gphh1EVBv52eQ9R7tmmu9EEPIMWWbgdG5FclawDbiUuFnH7MsPT71y6NKWoxUfnzBivLgsdymruYUmJltXvH27pPeZoZetuAzzQFfM25ctvU",
|
||||||
|
"qi": "GbOKVpISiuLNPYzTSfQv0pYDDKwtdxyRlSPwU-0rjuYdzsTI6p4QRIbb-8pdWDTuBhmOuB3NUGGPRmeLn8187Z1iTsRSy7bjWmwqcXfOtc22En7OfTUhaUn-p83u9-NpFyGIkB8smZ-kV_g3DEwxovWvvcF3bAajvQCNrbqsOLQ"
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
|
||||||
|
"git.rootprojects.org/root/keypairs/keyfetch"
|
||||||
|
"git.rootprojects.org/root/libauth"
|
||||||
|
"git.rootprojects.org/root/libauth/chiauth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
whitelist, err := keyfetch.NewWhitelist([]string{"https://therootcompany.github.io/libauth/"})
|
||||||
|
if nil != err {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unauthenticated Routes
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Post("/api/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(`{ "message": "Hello, World!" }`))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Authenticated Routes
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
tokenVerifier := chiauth.NewTokenVerifier(chiauth.VerificationParams{
|
||||||
|
Issuers: whitelist,
|
||||||
|
Optional: true,
|
||||||
|
})
|
||||||
|
r.Use(tokenVerifier)
|
||||||
|
|
||||||
|
r.Post("/api/users/profile", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
jws, ok := ctx.Value(chiauth.JWSKey).(*libauth.JWS)
|
||||||
|
if !ok || !jws.Trusted {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := jws.Claims["sub"].(string)
|
||||||
|
|
||||||
|
b, _ := json.MarshalIndent(struct {
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}{
|
||||||
|
UserID: userID,
|
||||||
|
}, "", " ")
|
||||||
|
w.Write(append(b, '\n'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
bindAddr := ":3000"
|
||||||
|
fmt.Println("Listening on", bindAddr)
|
||||||
|
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println("Try this:")
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println("")
|
||||||
|
cwd, _ := os.Getwd()
|
||||||
|
fmt.Println(" pushd", cwd)
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println(" my_jwt=\"$(cat ./jwt.txt)\"")
|
||||||
|
fmt.Println(
|
||||||
|
strings.Join(
|
||||||
|
[]string{
|
||||||
|
" curl -X POST http://localhost:3000/api/users/profile",
|
||||||
|
" -H \"Authorization: Bearer ${my_jwt}\"",
|
||||||
|
" -H 'Content-Type: application/json'",
|
||||||
|
" --data-binary '{ \"foo\": \"bar\" }'",
|
||||||
|
},
|
||||||
|
" \\\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
fmt.Println("")
|
||||||
|
|
||||||
|
http.ListenAndServe(bindAddr, r)
|
||||||
|
}
|
Loading…
Reference in New Issue