Skip to content
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

Multiple OIDC identifiers for an identity don't work #3910

Open
5 tasks done
noells opened this issue May 3, 2024 · 0 comments
Open
5 tasks done

Multiple OIDC identifiers for an identity don't work #3910

noells opened this issue May 3, 2024 · 0 comments
Labels
bug Something is not working.

Comments

@noells
Copy link

noells commented May 3, 2024

Preflight checklist

Ory Network Project

No response

Describe the bug

One of the providers we use with Kratos is Sign in With Apple. Because of business reasons, we need to go over an App Transfer in apple. Basically, we need to switch from one account to the other. For this process, Apple provides a mechanism that allows you to generate the new sub that they will use to log in with Kratos. From Kratos perspective, we need to change the secrets for apple OIDC provider. Basically, what we need to do is replace the apple identifier with the new identifier issued by Apple for all apple identities.

What I was trying to do was to include both identifiers (let's call them old_sub and new_sub) in the identities as this would guarantee a downtime free migration. This, however, does not work properly in Kratos side. Let me share the problems I've found:

  1. The HTTP api does not allow to import credentials to existing identities. The documentation claims it is possible but I couldn't do this using any of the endpoints in the docs.
  2. As the API does not allow it, I went directly to DB level. As Kratos is stateless, I thought this would work. While I can include all the information in the DB and all HTTP requests for the identity work well (I can even see both identifiers in the credentials secion). It is not possible to log in with any of the identifiers later on. I get this error:
{"audience":"application","error":{"debug":"Unable to find credentials that match the given provider \"google\" and subject \"9876\".","message":"An internal server error occurred, please contact the system administrator","reason":"Unable to find matching OpenID Connect Credentials.","stack_trace":"\ngithub.com/ory/kratos/selfservice/strategy/oidc.(*Strategy).processLogin\n\t/project/selfservice/strategy/oidc/strategy_login.go:180\ngithub.com/ory/kratos/selfservice/strategy/oidc.
...

What I've found out is that I can replace the old_sub with the new_sub and the log in, in this scenario, works perfectly.

While the workaround of replacing one identifier with the other works, it does not guarantee a downtime free migration in our case. If both were supported, it is pretty easy to realise that while Kratos is running with the old secrets, the old_sub would work and, from the moment Kratos secrets are updated, the new_sub would work.

As this is not the case, downtime free is not guarantee here. This is a huge problem for us, as we have millions of users that could be potentially affected by the changes.

Is there any reason why an identity cannot have multiple identifiers for the same OIDC provider? This could be related with #3909 , where linking accounts with the same OIDC provider is not allowed either. Is there a reason why this kind of operations are not allowed?

Reproducing the bug

The easiest way to reproduce this is the following:

  1. Log in with one OIDC account. Access the Kratos identity and save the credential identifier
  2. Delete account from step 1
  3. Log in with a different OIDC account from step 1 but for the same OIDC provider
  4. Patch the new account adding the identifier saved from step 1. You can patch it by running
#1st step
                        UPDATE identity_credentials SET config="{\"providers\":[{\"initial_access_token\":\"\",\"initial_id_token\":\"\",\"initial_refresh_token\":\"\",\"provider\":\"apple\",\"subject\":\"${newSub}\"}]}"       
                        WHERE identity_id = YOUR_IDENTITY_ID_FROM_STEP_3

#2nd step
                        UPDATE identity_credential_identifiers SET identifier=${new_sub}       
                        WHERE identity_credential_id = CREDENTIAL_ID_UPDATED_IN_SQL_1stSTEP
  1. After this, you'll see that the identity has two identifiers but if you try to log in with any of them, you'll get the error I shared.

If you remove old identifier from DB, you'll see you are able to login with the new account and that the identity is the same.

Relevant log output

{"audience":"application","error":{"debug":"Unable to find credentials that match the given provider \"google\" and subject \"9876\".","message":"An internal server error occurred, please contact the system administrator","reason":"Unable to find matching OpenID Connect Credentials.","stack_trace":"\ngithub.com/ory/kratos/selfservice/strategy/oidc.(*Strategy).processLogin\n\t/project/selfservice/strategy/oidc/strategy_login.go:180\ngithub.com/ory/kratos/selfservice/strategy/oidc.(*Strategy).HandleCallback\n\t/project/selfservice/strategy/oidc/strategy.go:434\ngithub.com/ory/kratos/selfservice/strategy.disabledWriter\n\t/project/selfservice/strategy/handler.go:28\ngithub.com/ory/kratos/selfservice/strategy/oidc.(*Strategy).setRoutes.IsDisabled.func1\n\t/project/selfservice/strategy/handler.go:33\ngithub.com/ory/kratos/x.(*RouterPublic).GET.NoCacheHandle.func1\n\t/project/x/nocache.go:21\ngithub.com/ory/kratos/x.(*RouterPublic).Handle.NoCacheHandle.func1\n\t/project/x/nocache.go:21\ngithub.com/julienschmidt/httprouter.(*Router).ServeHTTP\n\t/go/pkg/mod/github.com/julienschmidt/[email protected]/router.go:387\ngithub.com/ory/nosurf.(*CSRFHandler).handleSuccess\n\t/go/pkg/mod/github.com/ory/[email protected]/handler.go:234\ngithub.com/ory/nosurf.(*CSRFHandler).ServeHTTP\n\t/go/pkg/mod/github.com/ory/[email protected]/handler.go:191\ngithub.com/urfave/negroni.(*Negroni).UseHandler.Wrap.func1\n\t/go/pkg/mod/github.com/urfave/[email protected]/negroni.go:46\ngithub.com/urfave/negroni.HandlerFunc.ServeHTTP\n\t/go/pkg/mod/github.com/urfave/[email protected]/negroni.go:29\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\t/go/pkg/mod/github.com/urfave/[email protected]/negroni.go:38\ngithub.com/ory/kratos/x.glob..func1\n\t/project/x/clean_url.go:15\ngithub.com/urfave/negroni.HandlerFunc.ServeHTTP\n\t/go/pkg/mod/github.com/urfave/[email protected]/negroni.go:29\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\t/go/pkg/mod/github.com/urfave/[email protected]/negroni.go:38\ngithub.com/rs/cors.(*Cors).ServeHTTP\n\t/go/pkg/mod/github.com/rs/[email protected]/cors.go:266\ngithub.com/ory/kratos/cmd/daemon.servePublic.func1\n\t/project/cmd/daemon/serve.go:114\ngithub.com/urfave/negroni.HandlerFunc.ServeHTTP\n\t/go/pkg/mod/github.com/urfave/[email protected]/negroni.go:29\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\t/go/pkg/mod/github.com/urfave/[email protected]/negroni.go:38\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2136\ngithub.com/prometheus/client_golang/prometheus/promhttp.InstrumentHandlerResponseSize.func1\n\t/go/pkg/mod/github.com/prometheus/[email protected]/prometheus/promhttp/instrument_server.go:284\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2136\ngithub.com/prometheus/client_golang/prometheus/promhttp.InstrumentHandlerCounter.func1\n\t/go/pkg/mod/github.com/prometheus/[email protected]/prometheus/promhttp/instrument_server.go:142\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2136\ngithub.com/prometheus/client_golang/prometheus/promhttp.InstrumentHandlerDuration.func1\n\t/go/pkg/mod/github.com/prometheus/[email protected]/prometheus/promhttp/instrument_server.go:92\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2136\ngithub.com/prometheus/client_golang/prometheus/promhttp.InstrumentHandlerDuration.func2\n\t/go/pkg/mod/github.com/prometheus/[email protected]/prometheus/promhttp/instrument_server.go:104\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2136\ngithub.com/prometheus/client_golang/prometheus/promhttp.InstrumentHandlerRequestSize.func1\n\t/go/pkg/mod/github.com/prometheus/[email protected]/prometheus/promhttp/instrument_server.go:234\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2136\ngithub.com/ory/x/prometheusx.Metrics.Instrument.Metrics.instrumentHandlerStatusBucket.func1\n\t/go/pkg/mod/github.com/ory/[email protected]/prometheusx/metrics.go:115\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2136","status":"Internal Server Error","status_code":500},"file":"/go/pkg/mod/github.com/ory/[email protected]/logrusx/helper.go:125","func":"github.com/ory/x/logrusx.(*Logger).Logf","http_request":{"headers":{"accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","accept-encoding":"gzip, deflate, br, zstd","accept-language":"en-GB,en-US;q=0.9,en;q=0.8","connection":"keep-alive","cookie":"Value is sensitive and has been redacted. To see the value set config key \"log.leak_sensitive_values = true\" or environment variable \"LOG_LEAK_SENSITIVE_VALUES=true\".","sec-ch-ua":"\"Chromium\";v=\"124\", \"Google Chrome\";v=\"124\", \"Not-A.Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"macOS\"","sec-fetch-dest":"document","sec-fetch-mode":"navigate","sec-fetch-site":"cross-site","sec-fetch-user":"?1","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"},"host":"localhost:4433","method":"GET","path":"/self-service/methods/oidc/callback/google","query":"Value is sensitive and has been redacted. To see the value set config key \"log.leak_sensitive_values = true\" or environment variable \"LOG_LEAK_SENSITIVE_VALUES=true\".","remote":"192.168.65.1:19797","scheme":"http"},"level":"error","msg":"An error occurred and is being forwarded to the error user interface.","otel":{"span_id":"730ea6593b74d6da","trace_id":"6ac5e57760212891d1deb5a13ef4c024"},"service_name":"Ory Kratos","service_version":"v1.1.0","time":"2024-04-24T08:00:14.443172595Z"}

Relevant configuration

No response

Version

v1.1.0

On which operating system are you observing this issue?

Linux

In which environment are you deploying?

Docker Compose

Additional Context

No response

@noells noells added the bug Something is not working. label May 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something is not working.
Projects
None yet
Development

No branches or pull requests

1 participant