---
title: Account Linking
description: "Linking attaches an additional identity — an [OAuth2](https://namazustudios.com/glossary/oauth2/) provider, an [OIDC](https://namazustudios.com/glossary/oidc/) provider, or an email/username and password — to a [User](https://namazustudios.com/glossary/user/) that already has an active [Session](https://namazustudios.com/glossary/session/). It's a separate, explicit action from logging in, and it's the only way…"
url: "https://namazustudios.com/docs/namazu-elements-core/user-authentication-sign-in/account-linking/"
updated: "2026-08-14"
category: Namazu Elements Core Features / User Authentication / Sign In
---

# Account Linking

Linking attaches an additional identity — an [OAuth2](https://namazustudios.com/glossary/oauth2/) provider, an [OIDC](https://namazustudios.com/glossary/oidc/) provider, or an email/username and password — to a [User](https://namazustudios.com/glossary/user/) that already has an active [Session](https://namazustudios.com/glossary/session/). It's a separate, explicit action from logging in, and it's the only way to combine two identities into one account.

Note

Logging in via [OIDC](https://namazustudios.com/glossary/oidc/) or [OAuth2](https://namazustudios.com/glossary/oauth2/) never merges into a [Session](https://namazustudios.com/glossary/session/) you already have. Login resolves purely from the token's claims — if the provider identity has been seen before, it signs into that [User](https://namazustudios.com/glossary/user/); if not, it creates a brand-new [User](https://namazustudios.com/glossary/user/). So if you create an anonymous [Session](https://namazustudios.com/glossary/session/) via [Signup](../user-authentication-in-elements) and then log in with, say, Twitch, you get a second, unrelated account, not your original anonymous account with Twitch attached. To attach Twitch to the anonymous account instead, call the linking endpoints below *while the anonymous [Session](https://namazustudios.com/glossary/session/) is still active*, rather than logging in with Twitch directly.

## Precondition: an active [User](https://namazustudios.com/glossary/user/) [Session](https://namazustudios.com/glossary/session/)

Every linking endpoint requires the caller to already hold a [Session](https://namazustudios.com/glossary/session/) at the `User` or `SUPERUSER` level (an anonymous/`UNPRIVILEGED` [Session](https://namazustudios.com/glossary/session/) doesn't qualify). Calling any of them without one returns a `403 Forbidden` — "Authentication required to link accounts" for [OAuth2](https://namazustudios.com/glossary/oauth2/)/[OIDC](https://namazustudios.com/glossary/oidc/), "Authentication required to link credentials" for email/username-password. The identity is always attached to **the [User](https://namazustudios.com/glossary/user/) making the request**; there's no way to link on behalf of another [User](https://namazustudios.com/glossary/user/).

## Endpoints

### POST /[User](https://namazustudios.com/glossary/user/)/me/link/[OAuth2](https://namazustudios.com/glossary/oauth2/)

Links an [OAuth2](https://namazustudios.com/glossary/oauth2/) identity, validated the same way an [OAuth2](https://namazustudios.com/glossary/oauth2/) login is. Request body:

```
{
  "schemeId": "<OAuth2 auth scheme id>",
  "requestParameters": { "...": "..." },
  "profileId": "<optional>",
  "profileSelector": "<optional>"
}
```

If the external [id](https://namazustudios.com/glossary/id/) is already linked to a *different* [User](https://namazustudios.com/glossary/user/), the request fails. If it's already linked to the calling [User](https://namazustudios.com/glossary/user/), the call is [Idempotent](https://namazustudios.com/glossary/idempotent/) — no duplicate link is created. On success it returns a full [Session](https://namazustudios.com/glossary/session/), the same as a login call.

### POST /[User](https://namazustudios.com/glossary/user/)/me/link/[OIDC](https://namazustudios.com/glossary/oidc/)

Links an [OIDC](https://namazustudios.com/glossary/oidc/) identity from a [JWT](https://namazustudios.com/glossary/jwt/). Request body:

```
{
  "jwt": "<id_token>",
  "profileId": "<optional>",
  "profileSelector": "<optional>"
}
```

If the token's `sub` is already linked to a different [User](https://namazustudios.com/glossary/user/), the request fails. If the token's `email` claim is already linked to a different [User](https://namazustudios.com/glossary/user/), that's handled more leniently: the `sub` still links successfully, and the email simply isn't attached (no error). Linking this way does capture the provider's returned [Profile](https://namazustudios.com/glossary/profile/) claims into `User.linkedAccountProfiles`, the same audit trail described in [Users and Profiles](users-and-profiles) — but unlike a fresh anonymous login, it does **not** fill in the flat `displayName`/`firstName`/`lastName` fields.

### POST /[User](https://namazustudios.com/glossary/user/)/me/link/email-password

```
{
  "email": "user@example.com",
  "password": "..."
}
```

The email must already exist as a **verified** email UID on the calling [User](https://namazustudios.com/glossary/user/) — see [Email Verification](email-verification) for how to verify one first. If the email belongs to a different account, the request is rejected. On success it sets the password and returns the updated [User](https://namazustudios.com/glossary/user/).

### POST /[User](https://namazustudios.com/glossary/user/)/me/link/username-password

```
{
  "username": "...",
  "password": "..."
}
```

If the calling [User](https://namazustudios.com/glossary/user/) already has a different [name](https://namazustudios.com/glossary/name/) set, this is rejected — [name](https://namazustudios.com/glossary/name/) changes have to go through an explicit update, not linking. If the requested username is already taken by another [User](https://namazustudios.com/glossary/user/), it's rejected as well. Otherwise it claims the username and sets the password, returning the updated [User](https://namazustudios.com/glossary/user/).

## What happens on a conflict

In every case, an identity already linked to **another** [User](https://namazustudios.com/glossary/user/) is rejected rather than silently reassigned or merged — Elements never combines two existing accounts into one by linking. If you need one physical player to end up with a single account after using two different identities independently, that has to be handled as a deliberate migration, not a linking call.

An identity already linked to the **same** [User](https://namazustudios.com/glossary/user/) that's making the request is treated as a no-op success rather than an error, so clients don't need to check "is this already linked?" before calling these endpoints.
