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
|
# oidc discovery http://localhost:9998/.well-known/openid-configuration
|
||||||
CAOS_OIDC_DEV=1 go run github.com/caos/oidc/example/server/default
|
CAOS_OIDC_DEV=1 go run github.com/caos/oidc/example/server/default
|
||||||
# start oidc web client
|
# 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
|
- browser http://localhost:5556/login will redirect to op server
|
||||||
|
|
|
@ -17,16 +17,16 @@ func main() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
port := "9998"
|
port := "9998"
|
||||||
config := &op.Config{
|
config := &op.Config{
|
||||||
Issuer: "http://localhost:9998/",
|
Issuer: "http://localhost:9998/path/to/my/server",
|
||||||
CryptoKey: sha256.Sum256([]byte("test")),
|
CryptoKey: sha256.Sum256([]byte("test")),
|
||||||
}
|
}
|
||||||
storage := mock.NewAuthStorage()
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
router := handler.HttpHandler().(*mux.Router)
|
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)
|
router.Methods("POST").Path("/login").HandlerFunc(HandleCallback)
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: ":" + port,
|
Addr: ":" + port,
|
||||||
|
@ -68,5 +68,5 @@ func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
||||||
func HandleCallback(w http.ResponseWriter, r *http.Request) {
|
func HandleCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
client := r.FormValue("client")
|
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 {
|
func CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfiguration {
|
||||||
return &oidc.DiscoveryConfiguration{
|
return &oidc.DiscoveryConfiguration{
|
||||||
Issuer: c.Issuer(),
|
Issuer: c.Issuer(),
|
||||||
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()),
|
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||||
TokenEndpoint: c.TokenEndpoint().Absolute(c.Issuer()),
|
TokenEndpoint: c.TokenEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||||
IntrospectionEndpoint: c.IntrospectionEndpoint().Absolute(c.Issuer()),
|
IntrospectionEndpoint: c.IntrospectionEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||||
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()),
|
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||||
RevocationEndpoint: c.RevocationEndpoint().Absolute(c.Issuer()),
|
RevocationEndpoint: c.RevocationEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||||
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(c.Issuer()),
|
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||||
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
JwksURI: c.KeysEndpoint().Absolute(getIssuerHost(c.Issuer())),
|
||||||
ScopesSupported: Scopes(c),
|
ScopesSupported: Scopes(c),
|
||||||
ResponseTypesSupported: ResponseTypes(c),
|
ResponseTypesSupported: ResponseTypes(c),
|
||||||
GrantTypesSupported: GrantTypes(c),
|
GrantTypesSupported: GrantTypes(c),
|
||||||
|
|
|
@ -15,6 +15,15 @@ func NewEndpointWithURL(path, url string) Endpoint {
|
||||||
return Endpoint{path: path, url: url}
|
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 {
|
func (e Endpoint) Relative() string {
|
||||||
return relativeEndpoint(e.path)
|
return relativeEndpoint(e.path)
|
||||||
}
|
}
|
||||||
|
|
45
pkg/op/op.go
45
pkg/op/op.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
|
@ -68,9 +69,15 @@ func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router
|
||||||
handlers.AllowedHeaders([]string{"authorization", "content-type"}),
|
handlers.AllowedHeaders([]string{"authorization", "content-type"}),
|
||||||
handlers.AllowedOriginValidator(allowAllOrigins),
|
handlers.AllowedOriginValidator(allowAllOrigins),
|
||||||
))
|
))
|
||||||
router.HandleFunc(healthEndpoint, healthHandler)
|
|
||||||
router.HandleFunc(readinessEndpoint, readyHandler(o.Probes()))
|
issuerPath := getIssuerPath(o.Issuer())
|
||||||
router.HandleFunc(oidc.DiscoveryEndpoint, discoveryHandler(o, o.Signer()))
|
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.Handle(o.AuthorizationEndpoint().Relative(), intercept(authorizeHandler(o)))
|
||||||
router.NewRoute().Path(o.AuthorizationEndpoint().Relative()+"/callback").Queries("id", "{id}").Handler(intercept(authorizeCallbackHandler(o)))
|
router.NewRoute().Path(o.AuthorizationEndpoint().Relative()+"/callback").Queries("id", "{id}").Handler(intercept(authorizeCallbackHandler(o)))
|
||||||
router.Handle(o.TokenEndpoint().Relative(), intercept(tokenHandler(o)))
|
router.Handle(o.TokenEndpoint().Relative(), intercept(tokenHandler(o)))
|
||||||
|
@ -82,6 +89,36 @@ func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router
|
||||||
return 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 {
|
type Config struct {
|
||||||
Issuer string
|
Issuer string
|
||||||
CryptoKey [32]byte
|
CryptoKey [32]byte
|
||||||
|
@ -114,7 +151,7 @@ func NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opO
|
||||||
o := &openidProvider{
|
o := &openidProvider{
|
||||||
config: config,
|
config: config,
|
||||||
storage: storage,
|
storage: storage,
|
||||||
endpoints: DefaultEndpoints,
|
endpoints: NewDefaultEndpoints(config.Issuer),
|
||||||
timer: make(<-chan time.Time),
|
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