does anyone have any insight into how to handle ac...
# ory-selfhosting
c
does anyone have any insight into how to handle account linking during the login flow? currently the only way I can perform post account link actions during login flow is by using an after login hook and inspecting the
ctx.flow.request_url
to determine what oidc provider the user was attempting login with that prompted the account linking step. my goal is to perform some internal account synchronization steps based on what oidc provider they are trying to link to their existing account. in the context of the after login hook it doesn't seem like I have access to the oidc provider details like the after registration hook, which presents another problem as I am unable to access openid account details for the secondary account being linked.
m
Hey @cold-insurance-50617 For Ory Kratos there is something built-in: https://www.ory.sh/docs/kratos/social-signin/link-multiple-provider-account I would discourage automatic account linking (aka linking without the user proving that they own the account), as that can lead to account takeover. Sure, there are providers (like e.g. Google) where it is less problematic, but even for those there are edge-cases where it can become problematic.
c
ok thanks for the reply, is the recommendation of the ory team to just not support account linking at all or this there a recommended pattern that allows joining accounts while limiting the risk of account takeover
m
Hello @cold-insurance-50617 the recommendation is to use the account linking method that is implemented and documented here: https://www.ory.sh/docs/kratos/social-signin/link-multiple-provider-account Quoting from the docs, this is how it works:
1. The user creates an account with the identifier
<mailto:alice@example.com|alice@example.com>
and a password.
2. When signing in later, the user signs in with a social sign-in provider. That social sign-in account (through the OIDC userinfo endpoint or the identity token) contains the same identifier
<mailto:alice@example.com|alice@example.com>
.
3. Since the identifier already exists, the user can't be logged in directly. Instead, the user will be prompted to enter the password chosen in step 1.
4. After entering the correct password, the social sign-in is linked to the user's account. Now they can sign in with either password or social sign-in provider.
please let me know if that is clear, or if you understand something different under "account linking"
c
@magnificent-energy-493 this is the flow I am currently implementing, relying on ory to identify email collisions and only linking accounts where that is the case. my original post was about the hooks available during this flow, I need to perform some internal business logic when a user registers via discord for example, and when that happens via an account link in the flow you described its a little confusing where to place this logic as there doesn't seem to a hook for it. the after registration hook will fire, but it does so before the user confirms ownership via the secondary account (google in the example I provider) what I am looking for is a hook of sorts that fires when the user has successfully verified and linked the new provider please let me know if I am missing something
just wanted to follow up on this, the only way I was able to handle successful account linking in a hook was by using an
after login
hook and inspecting the
context.flow.ui.messages
for a
duplicateProvider
key and then extracting the
provider
property. I then use the users identity
id
to pull their
provider
subject
from the
identity_credentials
table. would love to know if there is a better way to hook into a successful account linking event and a cleaner way to access provider data used in the account linking process.
Copy code
func getDuplicateProvider(payload AfterLoginWebhookPayload) string {
	// Defensive check: payload or messages could be nil
	if payload.Context.Flow.UI.Messages == nil {
		return ""
	}
	var email = payload.Context.Identity.Traits.Email
	for _, msg := range *payload.Context.Flow.UI.Messages {
		var ctx = msg.Context
		if ctx == nil || ctx.DuplicateIdentifier == nil || ctx.Provider == nil {
			continue
		}
		if *ctx.DuplicateIdentifier == email {
			return *ctx.Provider
		}
	}
	return ""
}