fix: support issuer host with path
This commit is contained in:
parent
219ba4e038
commit
648fe9c11d
6 changed files with 183 additions and 16 deletions
|
@ -26,7 +26,7 @@ Check the `/example` folder where example code for different scenarios is locate
|
|||
# oidc discovery http://localhost:9998/.well-known/openid-configuration
|
||||
CAOS_OIDC_DEV=1 go run github.com/caos/oidc/example/server/default
|
||||
# start oidc web client
|
||||
CLIENT_ID=web CLIENT_SECRET=web ISSUER=http://localhost:9998/ SCOPES=openid PORT=5556 go run github.com/caos/oidc/example/client/app
|
||||
CLIENT_ID=web CLIENT_SECRET=web ISSUER=http://localhost:9998/path/to/my/server SCOPES=openid PORT=5556 go run github.com/caos/oidc/example/client/app
|
||||
```
|
||||
|
||||
- browser http://localhost:5556/login will redirect to op server
|
||||
|
|
|
@ -17,16 +17,16 @@ func main() {
|
|||
ctx := context.Background()
|
||||
port := "9998"
|
||||
config := &op.Config{
|
||||
Issuer: "http://localhost:9998/",
|
||||
Issuer: "http://localhost:9998/path/to/my/server",
|
||||
CryptoKey: sha256.Sum256([]byte("test")),
|
||||
}
|
||||
storage := mock.NewAuthStorage()
|
||||
handler, err := op.NewOpenIDProvider(ctx, config, storage, op.WithCustomTokenEndpoint(op.NewEndpoint("test")))
|
||||
handler, err := op.NewOpenIDProvider(ctx, config, storage, op.WithCustomTokenEndpoint(op.NewEndpointWithIssuersPath(config.Issuer, "test")))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
router := handler.HttpHandler().(*mux.Router)
|
||||
router.Methods("GET").Path("/login").HandlerFunc(HandleLogin)
|
||||
router.Methods("GET").Path("/path/to/my/server/login").HandlerFunc(HandleLogin)
|
||||
router.Methods("POST").Path("/login").HandlerFunc(HandleCallback)
|
||||
server := &http.Server{
|
||||
Addr: ":" + port,
|
||||
|
@ -68,5 +68,5 @@ func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
|||
func HandleCallback(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
client := r.FormValue("client")
|
||||
http.Redirect(w, r, "/authorize/callback?id="+client, http.StatusFound)
|
||||
http.Redirect(w, r, "/path/to/my/server/authorize/callback?id="+client, http.StatusFound)
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@ func Discover(w http.ResponseWriter, config *oidc.DiscoveryConfiguration) {
|
|||
func CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfiguration {
|
||||
return &oidc.DiscoveryConfiguration{
|
||||
Issuer: c.Issuer(),
|
||||
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()),
|
||||
TokenEndpoint: c.TokenEndpoint().Absolute(c.Issuer()),
|
||||
IntrospectionEndpoint: c.IntrospectionEndpoint().Absolute(c.Issuer()),
|
||||
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()),
|
||||
RevocationEndpoint: c.RevocationEndpoint().Absolute(c.Issuer()),
|
||||
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(c.Issuer()),
|
||||
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
||||
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||
TokenEndpoint: c.TokenEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||
IntrospectionEndpoint: c.IntrospectionEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||
RevocationEndpoint: c.RevocationEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||
JwksURI: c.KeysEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||
ScopesSupported: Scopes(c),
|
||||
ResponseTypesSupported: ResponseTypes(c),
|
||||
GrantTypesSupported: GrantTypes(c),
|
||||
|
|
|
@ -15,6 +15,15 @@ func NewEndpointWithURL(path, url string) Endpoint {
|
|||
return Endpoint{path: path, url: url}
|
||||
}
|
||||
|
||||
func NewEndpointWithIssuersPath(issuer, path string) Endpoint {
|
||||
issuerPath := getIssuerPath(issuer)
|
||||
if len(issuerPath) > 0 {
|
||||
issuerPath = issuerPath + "/"
|
||||
}
|
||||
|
||||
return Endpoint{path: issuerPath + path}
|
||||
}
|
||||
|
||||
func (e Endpoint) Relative() string {
|
||||
return relativeEndpoint(e.path)
|
||||
}
|
||||
|
|
45
pkg/op/op.go
45
pkg/op/op.go
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
|
@ -68,9 +69,15 @@ func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router
|
|||
handlers.AllowedHeaders([]string{"authorization", "content-type"}),
|
||||
handlers.AllowedOriginValidator(allowAllOrigins),
|
||||
))
|
||||
router.HandleFunc(healthEndpoint, healthHandler)
|
||||
router.HandleFunc(readinessEndpoint, readyHandler(o.Probes()))
|
||||
router.HandleFunc(oidc.DiscoveryEndpoint, discoveryHandler(o, o.Signer()))
|
||||
|
||||
issuerPath := getIssuerPath(o.Issuer())
|
||||
if len(issuerPath) > 0 {
|
||||
issuerPath = "/" + issuerPath
|
||||
}
|
||||
|
||||
router.HandleFunc(issuerPath+healthEndpoint, healthHandler)
|
||||
router.HandleFunc(issuerPath+readinessEndpoint, readyHandler(o.Probes()))
|
||||
router.HandleFunc(issuerPath+oidc.DiscoveryEndpoint, discoveryHandler(o, o.Signer()))
|
||||
router.Handle(o.AuthorizationEndpoint().Relative(), intercept(authorizeHandler(o)))
|
||||
router.NewRoute().Path(o.AuthorizationEndpoint().Relative()+"/callback").Queries("id", "{id}").Handler(intercept(authorizeCallbackHandler(o)))
|
||||
router.Handle(o.TokenEndpoint().Relative(), intercept(tokenHandler(o)))
|
||||
|
@ -82,6 +89,36 @@ func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router
|
|||
return router
|
||||
}
|
||||
|
||||
func NewDefaultEndpoints(issuer string) *endpoints {
|
||||
return &endpoints{
|
||||
Authorization: NewEndpointWithIssuersPath(issuer, defaultAuthorizationEndpoint),
|
||||
Token: NewEndpointWithIssuersPath(issuer, defaultTokenEndpoint),
|
||||
Introspection: NewEndpointWithIssuersPath(issuer, defaultIntrospectEndpoint),
|
||||
Userinfo: NewEndpointWithIssuersPath(issuer, defaultUserinfoEndpoint),
|
||||
Revocation: NewEndpointWithIssuersPath(issuer, defaultRevocationEndpoint),
|
||||
EndSession: NewEndpointWithIssuersPath(issuer, defaultEndSessionEndpoint),
|
||||
JwksURI: NewEndpointWithIssuersPath(issuer, defaultKeysEndpoint),
|
||||
}
|
||||
}
|
||||
|
||||
func getIssuerHost(issuer string) string {
|
||||
split := strings.Split(issuer, "/")
|
||||
if len(split) > 3 {
|
||||
return strings.Join(split[:3], "/")
|
||||
} else {
|
||||
return issuer
|
||||
}
|
||||
}
|
||||
|
||||
func getIssuerPath(issuer string) string {
|
||||
split := strings.Split(issuer, "/")
|
||||
if len(split) > 3 && len(split[3]) > 0 {
|
||||
return strings.Join(split[3:], "/")
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Issuer string
|
||||
CryptoKey [32]byte
|
||||
|
@ -114,7 +151,7 @@ func NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opO
|
|||
o := &openidProvider{
|
||||
config: config,
|
||||
storage: storage,
|
||||
endpoints: DefaultEndpoints,
|
||||
endpoints: NewDefaultEndpoints(config.Issuer),
|
||||
timer: make(<-chan time.Time),
|
||||
}
|
||||
|
||||
|
|
121
pkg/op/op_test.go
Normal file
121
pkg/op/op_test.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package op
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetIssuerHost(t *testing.T) {
|
||||
type args struct {
|
||||
issuer string
|
||||
}
|
||||
type res struct {
|
||||
path string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "just domain, without trailing forward slash",
|
||||
args: args{
|
||||
issuer: "https://localhost",
|
||||
},
|
||||
res: res{
|
||||
path: "https://localhost",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "just domain, with trailing forward slash",
|
||||
args: args{
|
||||
issuer: "https://localhost/",
|
||||
},
|
||||
res: res{
|
||||
path: "https://localhost",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "domain with path 1",
|
||||
args: args{
|
||||
issuer: "https://localhost/custompath",
|
||||
},
|
||||
res: res{
|
||||
path: "https://localhost",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "domain with path 2",
|
||||
args: args{
|
||||
issuer: "https://localhost/custom/path",
|
||||
},
|
||||
res: res{
|
||||
path: "https://localhost",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotPath := getIssuerHost(tt.args.issuer)
|
||||
assert.Equal(t, tt.res.path, gotPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIssuerPath(t *testing.T) {
|
||||
type args struct {
|
||||
issuer string
|
||||
}
|
||||
type res struct {
|
||||
path string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "just domain, without trailing forward slash",
|
||||
args: args{
|
||||
issuer: "https://localhost",
|
||||
},
|
||||
res: res{
|
||||
path: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "just domain, with trailing forward slash",
|
||||
args: args{
|
||||
issuer: "https://localhost/",
|
||||
},
|
||||
res: res{
|
||||
path: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "domain with path 1",
|
||||
args: args{
|
||||
issuer: "https://localhost/custompath",
|
||||
},
|
||||
res: res{
|
||||
path: "custompath",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "domain with path 2",
|
||||
args: args{
|
||||
issuer: "https://localhost/custom/path",
|
||||
},
|
||||
res: res{
|
||||
path: "custom/path",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotPath := getIssuerPath(tt.args.issuer)
|
||||
assert.Equal(t, tt.res.path, gotPath)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue