better support for /device/callback redirect uris with public clients.
Signed-off-by: justin-slowik <justin.slowik@thermofisher.com>
This commit is contained in:
parent
f6d8427f32
commit
9882ea453f
6 changed files with 19 additions and 11 deletions
|
@ -98,8 +98,9 @@ staticClients:
|
||||||
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
|
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
|
||||||
# - id: example-device-client
|
# - id: example-device-client
|
||||||
# redirectURIs:
|
# redirectURIs:
|
||||||
# - /dex/device/callback
|
# - /device/callback
|
||||||
# name: 'Static Client for Device Flow'
|
# name: 'Static Client for Device Flow'
|
||||||
|
# public: true
|
||||||
connectors:
|
connectors:
|
||||||
- type: mockCallback
|
- type: mockCallback
|
||||||
id: mock
|
id: mock
|
||||||
|
|
|
@ -374,7 +374,7 @@ func (s *Server) verifyUserCode(w http.ResponseWriter, r *http.Request) {
|
||||||
q.Set("client_secret", deviceRequest.ClientSecret)
|
q.Set("client_secret", deviceRequest.ClientSecret)
|
||||||
q.Set("state", deviceRequest.UserCode)
|
q.Set("state", deviceRequest.UserCode)
|
||||||
q.Set("response_type", "code")
|
q.Set("response_type", "code")
|
||||||
q.Set("redirect_uri", path.Join(s.issuerURL.Path, "/device/callback"))
|
q.Set("redirect_uri", "/device/callback")
|
||||||
q.Set("scope", strings.Join(deviceRequest.Scopes, " "))
|
q.Set("scope", strings.Join(deviceRequest.Scopes, " "))
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ func TestDeviceCallback(t *testing.T) {
|
||||||
baseAuthCode := storage.AuthCode{
|
baseAuthCode := storage.AuthCode{
|
||||||
ID: "somecode",
|
ID: "somecode",
|
||||||
ClientID: "testclient",
|
ClientID: "testclient",
|
||||||
RedirectURI: "/device/callback",
|
RedirectURI: deviceCallbackURI,
|
||||||
Nonce: "",
|
Nonce: "",
|
||||||
Scopes: []string{"openid", "profile", "email"},
|
Scopes: []string{"openid", "profile", "email"},
|
||||||
ConnectorID: "mock",
|
ConnectorID: "mock",
|
||||||
|
@ -194,7 +194,7 @@ func TestDeviceCallback(t *testing.T) {
|
||||||
testAuthCode: storage.AuthCode{
|
testAuthCode: storage.AuthCode{
|
||||||
ID: "somecode",
|
ID: "somecode",
|
||||||
ClientID: "testclient",
|
ClientID: "testclient",
|
||||||
RedirectURI: "/device/callback",
|
RedirectURI: deviceCallbackURI,
|
||||||
Nonce: "",
|
Nonce: "",
|
||||||
Scopes: []string{"openid", "profile", "email"},
|
Scopes: []string{"openid", "profile", "email"},
|
||||||
ConnectorID: "pic",
|
ConnectorID: "pic",
|
||||||
|
@ -210,7 +210,7 @@ func TestDeviceCallback(t *testing.T) {
|
||||||
testAuthCode: storage.AuthCode{
|
testAuthCode: storage.AuthCode{
|
||||||
ID: "somecode",
|
ID: "somecode",
|
||||||
ClientID: "testclient",
|
ClientID: "testclient",
|
||||||
RedirectURI: "/device/callback",
|
RedirectURI: deviceCallbackURI,
|
||||||
Nonce: "",
|
Nonce: "",
|
||||||
Scopes: []string{"openid", "profile", "email"},
|
Scopes: []string{"openid", "profile", "email"},
|
||||||
ConnectorID: "pic",
|
ConnectorID: "pic",
|
||||||
|
@ -336,7 +336,7 @@ func TestDeviceCallback(t *testing.T) {
|
||||||
client := storage.Client{
|
client := storage.Client{
|
||||||
ID: "testclient",
|
ID: "testclient",
|
||||||
Secret: "",
|
Secret: "",
|
||||||
RedirectURIs: []string{"/device/callback"},
|
RedirectURIs: []string{deviceCallbackURI},
|
||||||
}
|
}
|
||||||
if err := s.storage.CreateClient(client); err != nil {
|
if err := s.storage.CreateClient(client); err != nil {
|
||||||
t.Fatalf("failed to create client: %v", err)
|
t.Fatalf("failed to create client: %v", err)
|
||||||
|
|
|
@ -114,6 +114,10 @@ const (
|
||||||
scopeCrossClientPrefix = "audience:server:client_id:"
|
scopeCrossClientPrefix = "audience:server:client_id:"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
deviceCallbackURI = "/device/callback"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
redirectURIOOB = "urn:ietf:wg:oauth:2.0:oob"
|
redirectURIOOB = "urn:ietf:wg:oauth:2.0:oob"
|
||||||
)
|
)
|
||||||
|
@ -433,6 +437,9 @@ func (s *Server) parseAuthorizationRequest(r *http.Request) (*storage.AuthReques
|
||||||
description := fmt.Sprintf("Unregistered redirect_uri (%q).", redirectURI)
|
description := fmt.Sprintf("Unregistered redirect_uri (%q).", redirectURI)
|
||||||
return nil, &authErr{"", "", errInvalidRequest, description}
|
return nil, &authErr{"", "", errInvalidRequest, description}
|
||||||
}
|
}
|
||||||
|
if redirectURI == deviceCallbackURI && client.Public {
|
||||||
|
redirectURI = s.issuerURL.Path + deviceCallbackURI
|
||||||
|
}
|
||||||
|
|
||||||
// From here on out, we want to redirect back to the client with an error.
|
// From here on out, we want to redirect back to the client with an error.
|
||||||
newErr := func(typ, format string, a ...interface{}) *authErr {
|
newErr := func(typ, format string, a ...interface{}) *authErr {
|
||||||
|
@ -574,7 +581,7 @@ func validateRedirectURI(client storage.Client, redirectURI string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirectURI == redirectURIOOB {
|
if redirectURI == redirectURIOOB || redirectURI == deviceCallbackURI{
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -309,7 +309,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
|
||||||
handleFunc("/device/auth/verify_code", s.verifyUserCode)
|
handleFunc("/device/auth/verify_code", s.verifyUserCode)
|
||||||
handleFunc("/device/code", s.handleDeviceCode)
|
handleFunc("/device/code", s.handleDeviceCode)
|
||||||
handleFunc("/device/token", s.handleDeviceToken)
|
handleFunc("/device/token", s.handleDeviceToken)
|
||||||
handleFunc("/device/callback", s.handleDeviceCallback)
|
handleFunc(deviceCallbackURI, s.handleDeviceCallback)
|
||||||
r.HandleFunc(path.Join(issuerURL.Path, "/callback"), func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc(path.Join(issuerURL.Path, "/callback"), func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Strip the X-Remote-* headers to prevent security issues on
|
// Strip the X-Remote-* headers to prevent security issues on
|
||||||
// misconfigured authproxy connector setups.
|
// misconfigured authproxy connector setups.
|
||||||
|
|
|
@ -1317,8 +1317,8 @@ func TestOAuth2DeviceFlow(t *testing.T) {
|
||||||
//Add the Clients to the test server
|
//Add the Clients to the test server
|
||||||
client := storage.Client{
|
client := storage.Client{
|
||||||
ID: clientID,
|
ID: clientID,
|
||||||
//Secret: "testclientsecret",
|
RedirectURIs: []string{deviceCallbackURI},
|
||||||
RedirectURIs: []string{"/non-root-path/device/callback"},
|
Public: true,
|
||||||
}
|
}
|
||||||
if err := s.storage.CreateClient(client); err != nil {
|
if err := s.storage.CreateClient(client); err != nil {
|
||||||
t.Fatalf("failed to create client: %v", err)
|
t.Fatalf("failed to create client: %v", err)
|
||||||
|
@ -1421,7 +1421,7 @@ func TestOAuth2DeviceFlow(t *testing.T) {
|
||||||
ClientSecret: client.Secret,
|
ClientSecret: client.Secret,
|
||||||
Endpoint: p.Endpoint(),
|
Endpoint: p.Endpoint(),
|
||||||
Scopes: requestedScopes,
|
Scopes: requestedScopes,
|
||||||
RedirectURL: "/non-root-path/device/callback",
|
RedirectURL: deviceCallbackURI,
|
||||||
}
|
}
|
||||||
if len(tc.scopes) != 0 {
|
if len(tc.scopes) != 0 {
|
||||||
oauth2Config.Scopes = tc.scopes
|
oauth2Config.Scopes = tc.scopes
|
||||||
|
|
Reference in a new issue