Merge branch 'next' into next-main
This commit is contained in:
commit
d9487ef77d
118 changed files with 6091 additions and 981 deletions
|
@ -6,9 +6,9 @@ import (
|
|||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -43,7 +43,7 @@ var (
|
|||
|
||||
type login struct {
|
||||
authenticate authenticate
|
||||
router *mux.Router
|
||||
router chi.Router
|
||||
callback func(context.Context, string) string
|
||||
}
|
||||
|
||||
|
@ -57,9 +57,9 @@ func NewLogin(authenticate authenticate, callback func(context.Context, string)
|
|||
}
|
||||
|
||||
func (l *login) createRouter(issuerInterceptor *op.IssuerInterceptor) {
|
||||
l.router = mux.NewRouter()
|
||||
l.router.Path("/username").Methods("GET").HandlerFunc(l.loginHandler)
|
||||
l.router.Path("/username").Methods("POST").HandlerFunc(issuerInterceptor.HandlerFunc(l.checkLoginHandler))
|
||||
l.router = chi.NewRouter()
|
||||
l.router.Get("/username", l.loginHandler)
|
||||
l.router.With(issuerInterceptor.Handler).Post("/username", l.checkLoginHandler)
|
||||
}
|
||||
|
||||
type authenticate interface {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/go-chi/chi"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/oidc/v2/example/server/storage"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/example/server/storage"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -47,7 +47,7 @@ func main() {
|
|||
//be sure to create a proper crypto random key and manage it securely!
|
||||
key := sha256.Sum256([]byte("test"))
|
||||
|
||||
router := mux.NewRouter()
|
||||
router := chi.NewRouter()
|
||||
|
||||
//for simplicity, we provide a very small default page for users who have signed out
|
||||
router.HandleFunc(pathLoggedOut, func(w http.ResponseWriter, req *http.Request) {
|
||||
|
@ -76,7 +76,7 @@ func main() {
|
|||
|
||||
//regardless of how many pages / steps there are in the process, the UI must be registered in the router,
|
||||
//so we will direct all calls to /login to the login UI
|
||||
router.PathPrefix("/login/").Handler(http.StripPrefix("/login", l.router))
|
||||
router.Mount("/login/", http.StripPrefix("/login", l.router))
|
||||
|
||||
//we register the http handler of the OP on the root, so that the discovery endpoint (/.well-known/openid-configuration)
|
||||
//is served on the correct path
|
||||
|
@ -84,7 +84,7 @@ func main() {
|
|||
//if your issuer ends with a path (e.g. http://localhost:9998/custom/path/),
|
||||
//then you would have to set the path prefix (/custom/path/):
|
||||
//router.PathPrefix("/custom/path/").Handler(http.StripPrefix("/custom/path", provider.HttpHandler()))
|
||||
router.PathPrefix("/").Handler(provider.HttpHandler())
|
||||
router.Mount("/", provider)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: ":" + port,
|
||||
|
|
|
@ -1,21 +1,34 @@
|
|||
package exampleop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
type deviceAuthenticate interface {
|
||||
CheckUsernamePasswordSimple(username, password string) error
|
||||
op.DeviceAuthorizationStorage
|
||||
|
||||
// GetDeviceAuthorizationByUserCode resturns the current state of the device authorization flow,
|
||||
// identified by the user code.
|
||||
GetDeviceAuthorizationByUserCode(ctx context.Context, userCode string) (*op.DeviceAuthorizationState, error)
|
||||
|
||||
// CompleteDeviceAuthorization marks a device authorization entry as Completed,
|
||||
// identified by userCode. The Subject is added to the state, so that
|
||||
// GetDeviceAuthorizatonState can use it to create a new Access Token.
|
||||
CompleteDeviceAuthorization(ctx context.Context, userCode, subject string) error
|
||||
|
||||
// DenyDeviceAuthorization marks a device authorization entry as Denied.
|
||||
DenyDeviceAuthorization(ctx context.Context, userCode string) error
|
||||
}
|
||||
|
||||
type deviceLogin struct {
|
||||
|
@ -23,14 +36,14 @@ type deviceLogin struct {
|
|||
cookie *securecookie.SecureCookie
|
||||
}
|
||||
|
||||
func registerDeviceAuth(storage deviceAuthenticate, router *mux.Router) {
|
||||
func registerDeviceAuth(storage deviceAuthenticate, router chi.Router) {
|
||||
l := &deviceLogin{
|
||||
storage: storage,
|
||||
cookie: securecookie.New(securecookie.GenerateRandomKey(32), nil),
|
||||
}
|
||||
|
||||
router.HandleFunc("", l.userCodeHandler)
|
||||
router.Path("/login").Methods(http.MethodPost).HandlerFunc(l.loginHandler)
|
||||
router.HandleFunc("/", l.userCodeHandler)
|
||||
router.Post("/login", l.loginHandler)
|
||||
router.HandleFunc("/confirm", l.confirmHandler)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
type login struct {
|
||||
authenticate authenticate
|
||||
router *mux.Router
|
||||
router chi.Router
|
||||
callback func(context.Context, string) string
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,9 @@ func NewLogin(authenticate authenticate, callback func(context.Context, string)
|
|||
}
|
||||
|
||||
func (l *login) createRouter(issuerInterceptor *op.IssuerInterceptor) {
|
||||
l.router = mux.NewRouter()
|
||||
l.router.Path("/username").Methods("GET").HandlerFunc(l.loginHandler)
|
||||
l.router.Path("/username").Methods("POST").HandlerFunc(issuerInterceptor.HandlerFunc(l.checkLoginHandler))
|
||||
l.router = chi.NewRouter()
|
||||
l.router.Get("/username", l.loginHandler)
|
||||
l.router.Post("/username", issuerInterceptor.HandlerFunc(l.checkLoginHandler))
|
||||
}
|
||||
|
||||
type authenticate interface {
|
||||
|
|
|
@ -4,13 +4,16 @@ import (
|
|||
"crypto/sha256"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/exp/slog"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/oidc/v2/example/server/storage"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/example/server/storage"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -31,26 +34,33 @@ type Storage interface {
|
|||
deviceAuthenticate
|
||||
}
|
||||
|
||||
// simple counter for request IDs
|
||||
var counter atomic.Int64
|
||||
|
||||
// SetupServer creates an OIDC server with Issuer=http://localhost:<port>
|
||||
//
|
||||
// Use one of the pre-made clients in storage/clients.go or register a new one.
|
||||
func SetupServer(issuer string, storage Storage, extraOptions ...op.Option) *mux.Router {
|
||||
func SetupServer(issuer string, storage Storage, logger *slog.Logger, wrapServer bool, extraOptions ...op.Option) chi.Router {
|
||||
// the OpenID Provider requires a 32-byte key for (token) encryption
|
||||
// be sure to create a proper crypto random key and manage it securely!
|
||||
key := sha256.Sum256([]byte("test"))
|
||||
|
||||
router := mux.NewRouter()
|
||||
router := chi.NewRouter()
|
||||
router.Use(logging.Middleware(
|
||||
logging.WithLogger(logger),
|
||||
logging.WithIDFunc(func() slog.Attr {
|
||||
return slog.Int64("id", counter.Add(1))
|
||||
}),
|
||||
))
|
||||
|
||||
// for simplicity, we provide a very small default page for users who have signed out
|
||||
router.HandleFunc(pathLoggedOut, func(w http.ResponseWriter, req *http.Request) {
|
||||
_, err := w.Write([]byte("signed out successfully"))
|
||||
if err != nil {
|
||||
log.Printf("error serving logged out page: %v", err)
|
||||
}
|
||||
w.Write([]byte("signed out successfully"))
|
||||
// no need to check/log error, this will be handeled by the middleware.
|
||||
})
|
||||
|
||||
// creation of the OpenIDProvider with the just created in-memory Storage
|
||||
provider, err := newOP(storage, issuer, key, extraOptions...)
|
||||
provider, err := newOP(storage, issuer, key, logger, extraOptions...)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -62,17 +72,23 @@ func SetupServer(issuer string, storage Storage, extraOptions ...op.Option) *mux
|
|||
|
||||
// regardless of how many pages / steps there are in the process, the UI must be registered in the router,
|
||||
// so we will direct all calls to /login to the login UI
|
||||
router.PathPrefix("/login/").Handler(http.StripPrefix("/login", l.router))
|
||||
router.Mount("/login/", http.StripPrefix("/login", l.router))
|
||||
|
||||
router.PathPrefix("/device").Subrouter()
|
||||
registerDeviceAuth(storage, router.PathPrefix("/device").Subrouter())
|
||||
router.Route("/device", func(r chi.Router) {
|
||||
registerDeviceAuth(storage, r)
|
||||
})
|
||||
|
||||
handler := http.Handler(provider)
|
||||
if wrapServer {
|
||||
handler = op.NewLegacyServer(provider, *op.DefaultEndpoints)
|
||||
}
|
||||
|
||||
// we register the http handler of the OP on the root, so that the discovery endpoint (/.well-known/openid-configuration)
|
||||
// is served on the correct path
|
||||
//
|
||||
// if your issuer ends with a path (e.g. http://localhost:9998/custom/path/),
|
||||
// then you would have to set the path prefix (/custom/path/)
|
||||
router.PathPrefix("/").Handler(provider.HttpHandler())
|
||||
router.Mount("/", handler)
|
||||
|
||||
return router
|
||||
}
|
||||
|
@ -80,7 +96,7 @@ func SetupServer(issuer string, storage Storage, extraOptions ...op.Option) *mux
|
|||
// newOP will create an OpenID Provider for localhost on a specified port with a given encryption key
|
||||
// and a predefined default logout uri
|
||||
// it will enable all options (see descriptions)
|
||||
func newOP(storage op.Storage, issuer string, key [32]byte, extraOptions ...op.Option) (op.OpenIDProvider, error) {
|
||||
func newOP(storage op.Storage, issuer string, key [32]byte, logger *slog.Logger, extraOptions ...op.Option) (op.OpenIDProvider, error) {
|
||||
config := &op.Config{
|
||||
CryptoKey: key,
|
||||
|
||||
|
@ -114,10 +130,12 @@ func newOP(storage op.Storage, issuer string, key [32]byte, extraOptions ...op.O
|
|||
}
|
||||
handler, err := op.NewOpenIDProvider(issuer, config, storage,
|
||||
append([]op.Option{
|
||||
// we must explicitly allow the use of the http issuer
|
||||
//we must explicitly allow the use of the http issuer
|
||||
op.WithAllowInsecure(),
|
||||
// as an example on how to customize an endpoint this will change the authorization_endpoint from /authorize to /auth
|
||||
op.WithCustomAuthEndpoint(op.NewEndpoint("auth")),
|
||||
// Pass our logger to the OP
|
||||
op.WithLogger(logger.WithGroup("op")),
|
||||
}, extraOptions...)...,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -2,11 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/zitadel/oidc/v2/example/server/exampleop"
|
||||
"github.com/zitadel/oidc/v2/example/server/storage"
|
||||
"github.com/zitadel/oidc/v3/example/server/exampleop"
|
||||
"github.com/zitadel/oidc/v3/example/server/storage"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -20,16 +21,22 @@ func main() {
|
|||
// in this example it will be handled in-memory
|
||||
storage := storage.NewStorage(storage.NewUserStore(issuer))
|
||||
|
||||
router := exampleop.SetupServer(issuer, storage)
|
||||
logger := slog.New(
|
||||
slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelDebug,
|
||||
}),
|
||||
)
|
||||
router := exampleop.SetupServer(issuer, storage, logger, false)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: router,
|
||||
}
|
||||
log.Printf("server listening on http://localhost:%s/", port)
|
||||
log.Println("press ctrl+c to stop")
|
||||
logger.Info("server listening, press ctrl+c to stop", "addr", fmt.Sprintf("http://localhost:%s/", port))
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
if err != http.ErrServerClosed {
|
||||
logger.Error("server terminated", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ package storage
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -185,7 +185,7 @@ func WebClient(id, secret string, redirectURIs ...string) *Client {
|
|||
authMethod: oidc.AuthMethodBasic,
|
||||
loginURL: defaultLoginURL,
|
||||
responseTypes: []oidc.ResponseType{oidc.ResponseTypeCode},
|
||||
grantTypes: []oidc.GrantType{oidc.GrantTypeCode, oidc.GrantTypeRefreshToken},
|
||||
grantTypes: []oidc.GrantType{oidc.GrantTypeCode, oidc.GrantTypeRefreshToken, oidc.GrantTypeTokenExchange},
|
||||
accessTokenType: op.AccessTokenTypeBearer,
|
||||
devMode: false,
|
||||
idTokenUserinfoClaimsAssertion: false,
|
||||
|
|
|
@ -3,10 +3,11 @@ package storage
|
|||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -41,6 +42,19 @@ type AuthRequest struct {
|
|||
authTime time.Time
|
||||
}
|
||||
|
||||
// LogValue allows you to define which fields will be logged.
|
||||
// Implements the [slog.LogValuer]
|
||||
func (a *AuthRequest) LogValue() slog.Value {
|
||||
return slog.GroupValue(
|
||||
slog.String("id", a.ID),
|
||||
slog.Time("creation_date", a.CreationDate),
|
||||
slog.Any("scopes", a.Scopes),
|
||||
slog.String("response_type", string(a.ResponseType)),
|
||||
slog.String("app_id", a.ApplicationID),
|
||||
slog.String("callback_uri", a.CallbackURI),
|
||||
)
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetID() string {
|
||||
return a.ID
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
jose "github.com/go-jose/go-jose/v3"
|
||||
"github.com/google/uuid"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
// serviceKey1 is a public key which will be used for the JWT Profile Authorization Grant
|
||||
|
|
|
@ -4,10 +4,10 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
jose "github.com/go-jose/go-jose/v3"
|
||||
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
type multiStorage struct {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue