-
Notifications
You must be signed in to change notification settings - Fork 3
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
SAML Implementation in Kratos #3
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.
Nice work !
Some questions and a few comments. Mostly on English, Golang & code style.
@@ -1932,6 +1932,81 @@ | |||
] | |||
} | |||
}, | |||
"/self-service/saml/metadata":{ | |||
"get":{ | |||
"description": "This endpoint is for the IDP to obtain kratos metadata", |
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 style for the other descriptions is slightly different.
"This endpoints returns kratos metadata as part of a SAMl login" would be closer to what is done on other endpoints.
}, | ||
"/self-service/saml/idp":{ | ||
"get":{ | ||
"description": "This endpoint is to redirect the user to the idp auth flow", |
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.
Same as above: style for description text is different from other already existing endpoints.
"get":{ | ||
"description": "This endpoint is for the IDP to obtain kratos metadata", | ||
"operationId": "getSamlMetadata", | ||
"response": { |
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 missing a few bits on SAML flow, so this might be an irrelevant comment; is it expected there's no 200 response ?
@@ -2006,6 +2006,81 @@ | |||
] | |||
} | |||
}, | |||
"/self-service/saml/idp":{ | |||
"get":{ | |||
"description": "This endpoint is to redirect the user to the idp auth flow", |
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.
All other descriptions are something like "This endpoint + verb at the active form".
}, | ||
"mapper_url": { | ||
"title": "Jsonnet Mapper URL", | ||
"description": "The URL where the jsonnet source is located for mapping the provider's data to Ory Kratos data.", |
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.
misc english wording
"description": "The URL where the jsonnet source is located for mapping the provider's data to Ory Kratos data.", | |
"description": "Location for the jsonnet mapping between provider's data and Ory Kratos data", |
|
||
// does not need sorting because there is only one field | ||
c.SetCSRF(s.d.GenerateCSRFToken(r)) | ||
// AddSamlProviders(c, conf.Providers, message) |
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.
Commented code can be removed
return err | ||
} | ||
|
||
func (s *Strategy) CountActiveCredentials(cc map[identity.CredentialsType]identity.Credentials) (count int, err error) { |
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.
Personal preference: returning explicitly values instead of using named return argument. I do so, because I find it less error prone.
return | ||
} | ||
|
||
func (s *Strategy) CountActiveFirstFactorCredentials(cc map[identity.CredentialsType]identity.Credentials) (count int, err error) { |
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 function requires a bit of reading to fully understand what it does; it might be interesting to add a comment describing what it does.
// This file is called when the /ACS receives an assertion, the method below allows you to define whether to register or login the user indicated in the assertion | ||
//################# | ||
|
||
// Handle SAML Assertion and process to either login or register |
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.
golang standard comment format starts with variable of function name.
Kratos uses a different one:
kratos/selfservice/flow/recovery/handler.go
Line 203 in af1df63
// HTTP Cookies |
To be adapted based on your knowledge of ory coding style.
if err = s.processRegistration(w, r, registerFlow, provider, claims); err != nil { | ||
if i == nil { | ||
return nil, s.handleError(w, r, registerFlow, provider.Config().ID, nil, err) | ||
} else { |
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.
Unused else: No need to add a else
when last statement of an if
is a return.
Here and below
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.
Great work ! :D
The main problem I see is with the attribute mapping that only works with a fixed list of attributes names.
Otherwise it's about small code quality improvements. There are some pointed out by the linter that I didn't bother to comment as they are already shown in the PR builds.
I honestly think that we could already take reviews from the Ory team & community, as there are maybe some things about how they want the code to be organized.
"Microsoft Active Directory" | ||
] | ||
}, | ||
"public_cert_path": { |
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.
certificate_path ? Or public_key_certificate_path ?
} | ||
] | ||
}, | ||
"attributes_map": { |
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 attributes mapping is given in the config ? What if I have an attribute that is not provided here ? For example group
?
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.
For the moment, this part is not configurable, that is to say that the identity cannot have more fields than what is present here.
internal/httpclient/api/openapi.yaml
Outdated
@@ -1185,6 +1185,51 @@ paths: | |||
summary: Create a Logout URL for Browsers | |||
tags: | |||
- v0alpha2 | |||
/self-service/methods/saml/auth: |
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.
Not related to the doc but to the route of this call (I don't know where the route is defined).
In the Ory Documentation for the API, we can see that when there are different calls to make for browser vs API, they usually end the route with /browser
or /api
. Maybe the same can be applied 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.
We did it on purpose based on a misunderstanding, indeed we should change it back
selfservice/flow/saml/handler.go
Outdated
} | ||
|
||
// Key pair to encrypt and sign SAML requests | ||
keyPair, err := tls.LoadX509KeyPair(strings.Replace(c.SAMLProviders[len(c.SAMLProviders)-1].PublicCertPath, "file://", "", 1), strings.Replace(c.SAMLProviders[len(c.SAMLProviders)-1].PrivateKeyPath, "file://", "", 1)) |
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.
No Out Of Bound possible here ?
selfservice/flow/saml/handler.go
Outdated
var idpMetadata *samlidp.EntityDescriptor | ||
|
||
// We check if the metadata file is provided | ||
if c.SAMLProviders[len(c.SAMLProviders)-1].IDPInformation["idp_metadata_url"] != "" { |
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.
OOB ?
Also, you can check if a map entry is set with something like if value, ok := mymap["key"]; ok { ...
if c.SAMLProviders[len(c.SAMLProviders)-1].IDPInformation["idp_metadata_url"] != "" { | |
if metadataURL, ok := c.SAMLProviders[len(c.SAMLProviders)-1].IDPInformation["idp_metadata_url"]; ok { |
return | ||
} | ||
|
||
func (s *Strategy) CountActiveMultiFactorCredentials(cc map[identity.CredentialsType]identity.Credentials) (count int, err error) { |
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.
Not implemented ?
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.
Not yet. This implementation isn't required since a long time, so we had to do it like this on our last rebase.
return nil, err | ||
} | ||
} else { | ||
// The user already exist in database so we log him |
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 makes me think : what happens when the IDP deletes a user ? I guess the user remains in the SP forever... What are the consequences of that ? It should not be a problem at all, but we could investigate this case a bit
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 indeed what will happen but it is complicated for the SP to know that the IDP has deleted a user
return nil, nil | ||
} | ||
|
||
// Method not used but necessary to implement the interface |
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 find this weird but if you say so 🤨
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.
Here's the kind of error we get otherwise:
# github.com/ory/kratos/selfservice/strategy/saml/strategy
selfservice/strategy/saml/strategy/strategy_login.go:25:24: cannot use new(Strategy) (value of type *Strategy) as type login.Strategy in variable declaration:
*Strategy does not implement login.Strategy (missing Login method)
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.
What I found weird was that the method is called "Login", so I would expect that it is a pretty important method to implement in an IAM.
Edit: As we discussed, it's because of the rebase against the origin. It's a new method added to the interface, probably worth checking where this method is called, to ensure that it is safe to not implement it
is "gotest.tools/assert/cmp" | ||
) | ||
|
||
func TestGetAndDecryptAssertion(t *testing.T) { |
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.
Unrelated to the code : it would be interesting to discuss test strategy with the Ory community. It seems there is not a lot of info on what is considered a good level of testing on CONTRIBUING.md
file (should we cover all branches of a functions ? Should we test for edge cases ? Or simply testing the main intent of the function is OK ?)
@@ -3636,6 +3653,48 @@ | |||
] | |||
} | |||
}, | |||
"/self-service/methods/saml/auth": { |
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.
Same thing as before about the route : /browser
vs /api
routes
6b5b243
to
4272676
Compare
c7d132c
to
e70a0f3
Compare
… account using oidc. (ory#2496) Close ory#2444 Co-authored-by: aeneasr <[email protected]>
Closes ory#2380 Co-authored-by: aeneasr <[email protected]>
BREAKING CHANGE: SDK Method `getJsonSchema` was renamed to `getIdentitySchema`.
This PR resolves an issue with the redirect after a successful verification, if not specified.
Previously the context was not propagated to the http client. As a result the (instrumented) client did not find the existing span and the sapns for outgoing http request have been orphains. With this simple Fix they are now children of the corresponding webhook spans.
…2917) Request cookies were already available in raw form in the ctx.request_headers top-level argument to the Jsonnet snippet. Parsing cookies in Jsonnet is tedious and error-prone, though, so we parse them internally for convenience.
[skip ci]
BREAKING CHANGE: The `/admin/courier/messages` endpoint now uses `keysetpagination` instead.
Signed-off-by: ThibaultHerard <[email protected]> Co-authored-by: sebferrer <[email protected]> Co-authored-by: psauvage <[email protected]> Co-authored-by: alexGNX <[email protected]> Co-authored-by: Stoakes <[email protected]>
Signed-off-by: ThibaultHerard <[email protected]> Co-authored-by: sebferrer <[email protected]>
Signed-off-by: ThibaultHerard <[email protected]> Co-authored-by: sebferrer <[email protected]>
Signed-off-by: sebferrer <[email protected]> Co-authored-by: ThibaultHerard <[email protected]>
SAML Implementation
Related issue(s)
ory#1928