Přeskočit na hlavní obsah

Magic Link autentizace

Magic Link poskytuje autentizaci bez hesla prostřednictvím e-mailu. Uživatelé kliknou na bezpečný, časově omezený odkaz a přihlásí se bez zadávání hesla.

Jak to funguje​

┌──────────┐         ┌──────────┐         ┌──────────┐         ┌──────────┐
│ Uživatel │ │ Vaše App │ │ Klubero │ │ E-mail │
│ │ │ │ │ SSO │ │ Server │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ 1. Zadá email │ │ │
│ ──────────────────►│ │ │
│ │ │ │
│ │ 2. Požádá o magic │ │
│ │ link │ │
│ │ ──────────────────►│ │
│ │ │ │
│ │ │ 3. Odešle email │
│ │ │ ──────────────────►│
│ │ │ │
│ 4. Obdrží email │ │ │
│ ◄───────────────────────────────────────────────────────────│
│ │ │ │
│ 5. Klikne na link │ │ │
│ ────────────────────────────────────────► │
│ │ │ │
│ 6. Přihlášen, redirect do aplikace │ │
│ ◄───────────────────────────────────────│ │

Endpoint: POST /api/magiclink/send (anonymní)

curl -X POST https://your-sso-domain.com/api/magiclink/send \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"clientId": "my-app",
"redirectUri": "https://myapp.com/callback",
"responseType": "code",
"scopes": "openid profile email",
"state": "xyz",
"codeChallenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
"codeChallengeMethod": "S256"
}'

Tělo požadavku (CreateMagicLinkRequest, camelCase):

PolePovinnéPopis
emailAnoE-mailová adresa uživatele
clientIdNeClient ID registrované aplikace (validováno oproti registraci)
redirectUriNeRedirect URI (musí odpovídat registrovanému u klienta)
scopesNePožadované scopes (oddělené mezerou)
stateNeOAuth parametr state
responseTypeNeOAuth parametr response_type (např. code)
responseModeNeOAuth parametr response_mode
nonceNeOAuth parametr nonce (OIDC)
codeChallengeNePKCE code challenge
codeChallengeMethodNePKCE metoda (S256 nebo plain)

OAuth parametry se předávají jako samostatná pole (nikoli jako jeden returnUrl). Pokud jsou vyplněny clientId a redirectUri, jsou validovány oproti registrovanému klientovi a musí odpovídat.

Odpověď (MagicLinkResultModel):

{
"success": true,
"errorMessage": null,
"emailSent": true,
"expiresAt": null
}
Bezpečnostní poznámka

Pro platného klienta je odpověď vždy úspěšná (success: true), aby se zabránilo útokům typu email enumeration. Uživatel obdrží e-mail pouze pokud jeho účet existuje.

Pokud je zadán neplatný clientId nebo redirectUri, který neodpovídá registraci, vrátí endpoint HTTP 400 s ValidationProblemDetails, resp. tělem { "success": false, "errorMessage": "..." }.

Krok 2: Uživatel klikne na odkaz​

E-mail obsahuje odkaz v tomto formátu:

https://your-sso-domain.com/Account/MagicLink?token=BASE64URL_TOKEN&...

Odkaz obsahuje token a "ticket" nesoucí předané OAuth parametry (client, redirect URI, scopes, state, PKCE apod.), aby po přihlášení mohlo pokračovat autorizační flow.

Krok 3: Dokončení autentizace​

Když uživatel klikne na odkaz:

  1. Token je validován (existuje, nevypršel, nebyl použit)
  2. Uživatel je přihlášen pomocí cookie
  3. Pokud jsou přítomny OAuth parametry: Redirect na /connect/authorize (pokračuje normální flow)
  4. Pokud nejsou OAuth parametry: Redirect na uživatelský portál

Charakteristiky tokenu​

VlastnostHodnota
Formát256-bitová náhodná hodnota, URL-safe Base64 kódování
Životnost15 minut
PoužitíPouze jednorázové (spotřebován při kliknutí)
UloženíSHA256 hash uložen v databázi

Validace tokenu (bez spotřebování)​

Endpoint: GET /api/magiclink/validate?token= (anonymní)

Ověření platnosti tokenu bez jeho spotřebování:

curl "https://your-sso-domain.com/api/magiclink/validate?token=TOKEN_VALUE"

Odpověď (ValidateMagicLinkResult) — platný token:

{
"valid": true,
"userGuid": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"email": "j***@example.com",
"errorMessage": null,
"errorCode": null
}

Odpověď — neplatný token:

{
"valid": false,
"userGuid": null,
"email": null,
"errorMessage": "Token expired.",
"errorCode": 2
}

Pole errorCode je číslo podle tabulky v sekci Chybové kódy. Odpověď neobsahuje pole expiresAt.

Zrušení všech čekajících odkazů​

Endpoint: POST /api/magiclink/revoke-all (vyžaduje autentizaci)

Zrušení všech čekajících magic linků pro autentizovaného uživatele:

curl -X POST https://your-sso-domain.com/api/magiclink/revoke-all \
-H "Authorization: Bearer ACCESS_TOKEN"

Odpověď:

{
"revokedCount": 2
}

Chybové kódy​

Pole errorCode v odpovědi validate je číselná hodnota (výčet se serializuje jako číslo):

errorCodeVýznamPopisAkce uživatele
1TokenNotFoundToken neexistujePožádejte o nový magic link
2TokenExpiredToken vypršel (>15 minut)Požádejte o nový magic link
3TokenAlreadyUsedToken byl již použitPožádejte o nový magic link
4UserNotFoundUživatelský účet nenalezenKontaktujte podporu
5UserNotActiveUživatelský účet deaktivovánKontaktujte podporu
6UserLockedÚčet uzamčen (příliš mnoho neúspěšných pokusů)Počkejte nebo kontaktujte podporu
7InvalidTokenFormatToken je poškozenýPožádejte o nový magic link
8InvalidTicketNeplatný ticket s OAuth parametryPožádejte o nový magic link

Integrace s OAuth flow​

Pro integraci magic linku s vaším OAuth flow:

  1. Připravte si stejné OAuth parametry jako u běžného flow (clientId, redirectUri, scopes, state, případně PKCE)
  2. Předejte je jako samostatná pole v požadavku na /api/magiclink/send
  3. Po kliknutí na odkaz uživatel automaticky pokračuje v OAuth flow (SSO z ticketu obnoví autorizační požadavek)

Příklad:

curl -X POST https://your-sso-domain.com/api/magiclink/send \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"clientId": "my-app",
"redirectUri": "https://myapp.com/callback",
"responseType": "code",
"scopes": "openid profile email",
"state": "xyz",
"codeChallenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
"codeChallengeMethod": "S256"
}'