-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wellknown #427
Wellknown #427
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, this looks rock solid! I'll take another review pass later (or latest tomorrow). Thank you!
oauth2/handler.go
Outdated
} | ||
|
||
func (h *Handler) WellKnownHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { | ||
fmt.Printf("handler: %+v", h) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this
cmd/server/handler_oauth2_factory.go
Outdated
@@ -155,6 +155,8 @@ func newOAuth2Handler(c *config.Config, router *httprouter.Router, km jwk.Manage | |||
H: &herodot.JSON{}, | |||
AccessTokenLifespan: c.GetAccessTokenLifespan(), | |||
CookieStore: sessions.NewCookieStore(c.GetCookieSecret()), | |||
Issuer: c.Issuer, | |||
BaseURL: c.ClusterURL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the one part I was unsure of, where should the BaseURL
come from? Issuer might actually make the most sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, good question, this was actually a problem previously. The cluster url is usually set on the client side, not the server side. We could either use the issuer url, or make it easier to understand by renaming ISSUER env var to something like PUBLIC_URL. what do you think?
Sorry by the way for taking so long on responding, the last days were really busy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Issuer
is fine actually, thats what its generally called by most implementers. I suppose I should add a section to the docs that emphasizes its use. Would it be ok to remove BaseURL
then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sounds good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides adding it in the docs, also make the usage clear in hydra help host
:)
@arekkas this guy is good to go, I'm getting blocked on the coveralls, in order to increase the code coverage we would have to implement HTTP tests, which seems non-trivial. Let me know how you want to proceed here |
Any idea why travis is failing? |
oauth2/handler.go
Outdated
@@ -40,6 +45,9 @@ type Handler struct { | |||
|
|||
AccessTokenLifespan time.Duration | |||
CookieStore sessions.Store | |||
|
|||
Issuer string | |||
BaseURL string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is no longer needed right?
CI is good now, not sure what that blip was, also removed the unnecessary parameter from the oAuth handler |
Thanks! That's perfect. However, I have to be the mean maintainer here and insist on a test case for this :( It helps keep the code quality high and also test for potential errors not visible to code review. Testing this should be easy. For the oauth2 package, set up a Once that is done, I'll help you setting up the test for the JWK. |
ok no worries, I'll cook those up 👍 |
Ok @arekkas, I've added tests for both routes. Some additional changes were necessary per #433, having key ids as "public" or "private" is not sufficient when they are returned in the well-known response. The key id must be unique, so I added |
It's weird that the tests are failing, I don't think it's your code's fault, I tracked it as #443 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, just one more thing :)
cmd/server/helper_keys.go
Outdated
@@ -18,7 +18,7 @@ func createRS256KeysIfNotExist(c *config.Config, set, kid, use string) { | |||
if _, err := ctx.KeyManager.GetKey(set, kid); errors.Cause(err) == pkg.ErrNotFound { | |||
logrus.Infof("Key pair for signing %s is missing. Creating new one.", set) | |||
|
|||
keys, err := generator.Generate("") | |||
keys, err := generator.Generate(set) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure why I did generator.Generate("")
here. Can you confirm that the docker-compose example works with your patch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docker-compose example works, I went through all of the flows in the tutorial. So this changes the generic key names to public:id-token-foo
, which in turn changes a couple policies as documented here https://github.com/ory/hydra/pull/427/files#diff-08781ef00709d5059315ac5095fafabbR73 and https://github.com/ory/hydra/pull/427/files#diff-0704e4259cd334d9340c20bd02095628R8 . This has the downside of making the policy a bit more confusing. I suppose there aren't many policies that need to be written for the root keys, but let me know if you see a better way.
If we do want to go ahead with that, there are quite a few references to the kid
being "public" or "private", the easiest way to get by that may be to add a getKeyFromType
function that could be swapped out fairly easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this changes the generic key names to public:id-token-foo, which in turn changes a couple policies as documented here
Yeah that's what I thought which is why I asked the question. I don't know how I feel about that. Does it change the URLs which are used to look up the keys as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API contract would remain the same, but there may need to be some changes to /keys/set
on the backend.
Google:
https://www.googleapis.com/oauth2/v3/certs
MS:
https://login.windows.net/common/discovery/keys
They are using just a uuid for the kid
, so thats another option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see, I now also understand the change better. I think going with a timestamp, a uuid or some other unique identifier is the way to go here, the doubling of the name feels kinda weird and is also not unique, right?
However, this will impact the way we query for those keys within hydra, and maybe also in the SDK. So I think this is your suggestion for the key type?
I understand now the need for this, but it also complicates things a bit, so we need to document this, especially if we expose the KeyFromType functionality to the SDK/REST.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I wasn't able to get the point across yet :D
When booting Hydra on a new database, the signing keys for OIDC will be generated with:
{
set: "openid",
keys: [{kid: "private"}, {kid: "public"}]
}
In this case, the kids private and public are unique because the set does not have any other keys. Let's say the administrator of Hydra now adds a new keys to the set by accident:
{
set: "openid",
keys: [{kid: "private"}, {kid: "public"}, {kid: "public:just-any-key-whatever"}]
}
The private and public keys are still unique in this case - so no problem. Let's say the admin now wants to do some fancy key rotation. First, he renames the keys private and public:
{
set: "openid",
keys: [{kid: "private:old"}, {kid: "public:old"}]
}
next he adds the new keys:
{
set: "openid",
keys: [
{kid: "private"}, {kid: "public"},
{kid: "private:old"}, {kid: "public:old"}
]
}
Now, we have four keys. The old ones plus the fresh ones from key rotation. In a later stage I want to provide automatic key rotation, so this process would be automated by, for example, calling PUT /keys/rotate/openid
with the result being something like this:
{
set: "openid",
keys: [
{kid: "private"}, {kid: "public"},
{kid: "private:some-uuid-or-a-timestamp-or-something-else"}, {kid: "public:some-uuid-or-a-timestamp-or-something-else"}
]
}
Does this make more sense now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the confusion here is stemming from which keys are returned in the well-known
response. I am assuming that all of the public keys are returned, hence each set would have a kid
of public
and then you put all of those public keys in a single set and return them. Leading to 3 keys with the kid
of public
.
Whats interesting as I read through the OpenID spec http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata again, they don't specify which key types should be returned. Looking at Okta http://developer.okta.com/docs/api/resources/oauth2.html#get-authorization-server-keys they are returning just one key type, but with an added status
parameter.
I was seeing 3 keys from google and ms thinking it was consent, challenge, and idtoken
, but it may be past, present, future
of the same key type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow, this took me some time to get it. I didn't get that well-known is aggregating all those keys and putting them in the same set. Now this all makes sense, sorry for being so slow on understanding this :D
Ok, so how about we simply prefix the kid with the set id in the well-known endpoint? Or is that a hack job?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah so the more I look at this and think about it, I think you may have been right to assume that its just the idtoken
key that needs returned. I can't find any reason to have the consent
or challenge
keys in that set. I'll update to reflect that, sorry about the run around.
On the upside we've had a chance to look at key rotation within the sets. I'll leave that for another PR though 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, only returning the keys for openid connect makes sense IMO :)
oauth2/handler.go
Outdated
func (h *Handler) WellKnownHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { | ||
known := WellKnown{ | ||
Issuer: h.Issuer, | ||
AuthURL: h.Issuer + AuthPath, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel comfortable about this, what if the issuer is formated as http://foo.bar/foo/
and the AuthPath as /foo
, then we get http://foo.bar/foo//foo
as a result. For resolving this, see this SO question
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree I added a trim suffix to root.go
https://github.com/ory/hydra/pull/427/files#diff-ff7686b39bf90dc2520886fb874371a4R125 , do we want to do that for any of the other urls there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I totally missed that! Then this is fine of course :)
Thank you @grillz for staying on this issue for so long, I really appreciate it! |
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
@grillz to get this working I think you need to run glide update |
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
ok @arekkas I think we are close on this guy 😄, the one piece I couldn't figure out is how you are generating the swagger spec. I see https://github.com/ory/hydra/blob/master/gen-swagger.sh but thats generating a json file, are you just converting that to yaml? Let me know if there is anything else you see, and I will squash before we merge. |
Yeah I'm actually converting that manually via here (just paste the json and it will ask you if it's ok to convert to yaml). I'll take another 👀 tomorrow, 11.55pm over here :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you rock @grillz ! This looks awesome now! Don't be discouraged by the 23 comments, it's mostly nit picking and minor changes that should be resolvable in 5-10 minutes :)
Again, thank you a lot for contributing to Hydra and not running out of breath! Bigger changes require thorough code review and often a lot of time for both contributors as well as maintainers :) That way however I feel comfortable with releasing security-critical software that runs millions of requests :)
ps: you don't need to squash the changes (I can do that via the GH ui).
docs/api.swagger.yaml
Outdated
@@ -169,7 +233,6 @@ paths: | |||
description: The id of the OAuth 2.0 Client. | |||
name: id | |||
in: path | |||
required: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually required, was it removed by accident?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@arekkas I'm not sure what was going on with the swagger changes here, I just ran the gen-swagger.sh
and ran it through the conversion. I was hoping you knew lol
docs/api.swagger.yaml
Outdated
@@ -287,7 +350,6 @@ paths: | |||
description: The id of the OAuth 2.0 Client. | |||
name: id | |||
in: path | |||
required: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually required, was it removed by accident?
docs/api.swagger.yaml
Outdated
'302': { | ||
description: "found" | ||
} | ||
'302': {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without description, this causes a schema error, could you revert this change?
docs/api.swagger.yaml
Outdated
@@ -697,8 +757,7 @@ paths: | |||
type: string | |||
x-go-name: RefreshToken | |||
responses: | |||
'200': | |||
description: "ok" | |||
'200': {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without description, this causes a schema error, could you revert this change?
docs/api.swagger.yaml
Outdated
@@ -900,7 +958,6 @@ paths: | |||
description: The id of the policy. | |||
name: id | |||
in: path | |||
required: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually required, was it removed by accident?
jwk/handler_test.go
Outdated
|
||
JWKPath := "/.well-known/jwks.json" | ||
res, err := http.Get(testServer.URL + JWKPath) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be simplified with:
require.NoError(t, err, "problem in http request")
jwk/handler_test.go
Outdated
} | ||
|
||
resp := known.Key("public") | ||
if resp == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be simplified with:
require.NotNil(t, err, "Could not find key public")
oauth2/handler.go
Outdated
|
||
"github.com/Sirupsen/logrus" | ||
"strings" | ||
"time" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not super picky on this but since you probably will take another pass at this anyways, could you run goimports
on this file? Please on this file only so we get a clean changeset!
oauth2/handler.go
Outdated
|
||
// swagger:model WellKnown | ||
type WellKnown struct { | ||
Issuer string `json:"issuer"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Thanks for adding the model here :) You could copy the descriptions from the JWK RFC here so it's clear what each of those values are.
oauth2/handler.go
Outdated
r.GET(WellKnownPath, h.WellKnownHandler) | ||
} | ||
|
||
// swagger:route GET /.well-known/openid-configuration oauth2 WellKnownHandler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add the tag openid-connect here? :)
Signed-off-by: pbarker <pbarker@datapipe.com>
Signed-off-by: pbarker <pbarker@datapipe.com>
No worries @arekkas I'm happy to get it right 👍 , I updated all the changes you listed and ran https://github.com/ory/hydra/blob/master/oauth2/handler.go#L207 is producing Let me know if you want to find a more automated solution to those in this PR. Thanks! |
Awesome! Thanks! |
Adds discovery endpoints that were started in branch
fix-379
. Tested an working, opening this as a WIP, let me know if there is anything you want added/changed. Thanks