Увійти через AniHub

Вхід на ваш сайт акаунтом AniHub. Звичайний OpenID Connect — бібліотека підключається одним URL. Застосунок реєструєте самі.

Зареєструвати застосунок

За хвилину

Auth.js, Passport, Authlib, Spring Security, Keycloak — усі вміють OpenID Connect. Дайте бібліотеці одну адресу:

https://anihub.in.ua/.well-known/openid-configuration

Решту вона вичитає звідти сама. Конфіг, Auth.js / NextAuth:

providers: [
  {
    id: "anihub",
    name: "AniHub",
    type: "oidc",
    issuer: "https://anihub.in.ua",
    clientId: process.env.ANIHUB_CLIENT_ID,
    clientSecret: process.env.ANIHUB_CLIENT_SECRET,
    authorization: {
      params: { scope: "openid profile email" },
    },
  },
]

clientId і clientSecret — з картки застосунку в Налаштування → Розробникам.

Зберігайте sub: нікнейм і пошту користувач може змінити.

Адресу повернення бібліотеки (в Auth.js це /api/auth/callback/anihub) додайте до застосунку — збіг побайтовий.

Два типи застосунків

Питання одне: чи є у вас сервер, де може лежати секрет. Тип не змінюється після реєстрації.

Є серверна частина

Next.js з API-роутами, Django, Laravel, Express. Отримуєте client_secret і обмінюєте код зі свого сервера через Authorization: Basic.

Тільки браузер або телефон

SPA, мобільний чи десктопний застосунок. Секрету немає — у коді, що їде до користувача, він не сховається. Замість Basic надсилаєте client_id у тілі, захищає PKCE.

Кнопка входу

Готова кнопка для вашої сторінки входу — три варіанти під темний, світлий і акцентний фон. Підставте свій маршрут у href — далі все як завжди: користувач іде на ваш бекенд, а той починає авторизацію.

<script src="https://dev.anihub.in.ua/sdk/v1/anihub.js" defer></script>

<anihub-login variant="dark" href="/auth/anihub"></anihub-login>

Логотип уже всередині — завантажувати нічого. Стилі ізольовані в shadow DOM, тож ваш CSS кнопку не зачепить.

Веб-компонент — це скрипт із нашого домену, тобто залежність під час виконання. Якщо на сторінці входу сувора CSP або зайва залежність вам не потрібна — беріть чистий HTML: він працює, навіть якщо ми недоступні. Логотип у такому разі покладіть до себе, не тягніть із нашого домену.

Якщо бекенду немає

Той самий тег уміє провести вхід сам — authorization code + PKCE просто в браузері. Підходить публічним застосункам (тим, що без секрету); секрет у браузер не кладіть ніколи, token-ендпоінт його від публічного клієнта все одно не прийме.

<script src="https://dev.anihub.in.ua/sdk/v1/anihub.js" defer></script>

<anihub-login
  client-id="ваш client_id"
  redirect-uri="https://ваш-сайт/callback"
  scope="openid profile"
  flow="popup"
></anihub-login>

На сторінці, куди веде redirect-uri, завершіть обмін. Виклик безпечний навіть тоді, коли коду в адресі немає — тоді він поверне null.

AniHubLogin.handleCallback().then((token) => {
  if (!token) return;                 // сторінку відкрили не з авторизації
  return AniHubLogin.getUser(token.accessToken);
});

У режимі flow="popup" результат повертається у вікно, яке відкривало вхід — подією anihub-login на самому теґу. Refresh-токен видається лише зі скоупом offline_access.

Якщо робите вручну

1

PKCE-пара

ваш сервер

code_verifier тримайте в сесії. В URL іде тільки його хеш.

code_verifier=$(openssl rand -base64 60 | tr -d '=+/' | cut -c1-64)
code_challenge=$(printf '%s' "$code_verifier" \
  | openssl dgst -binary -sha256 | openssl base64 -A \
  | tr '+/' '-_' | tr -d '=')
2

Екран згоди

браузер

Редирект, не запит із коду.

GET
https://anihub.in.ua/api/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fexample.com%2Fauth%2Fcallback
  &scope=openid%20profile%20email
  &state=xyzABC123
  &nonce=n-0S6_WzA2Mj
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

Параметри

response_typestringобовʼязковий

Завжди code.

client_idstringобовʼязковий

З картки застосунку.

redirect_uriurlобовʼязковий

Побайтовий збіг із зареєстрованою.

scopestringобовʼязковий

Через пробіл, з openid.

statestringобовʼязковий

Повернемо як є. Звірте.

code_challengestringобовʼязковий

base64url(SHA-256(code_verifier)).

code_challenge_methodstringобовʼязковий

Тільки S256.

noncestring

Повернемо в id_token. Звірте.

promptstring

consent — питати згоду знову. none — без UI, інакше помилка.

3

Код на вашій адресі

ваш сервер
Callback
https://example.com/auth/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyzABC123

Звірте state. Не збігається — припиняйте. Відмова користувача приходить як error=access_denied.

4

Обмін коду на токени

ваш сервер

Форма, не JSON. Тільки з сервера — тут секрет.

curl -X POST https://anihub.in.ua/api/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://example.com/auth/callback \
  -d code_verifier="$CODE_VERIFIER"

Тіло запиту

grant_typestringобовʼязковий

authorization_code

codestringобовʼязковий

Одноразовий, 60 секунд.

redirect_uriurlобовʼязковий

Той самий рядок, що в кроці 2.

code_verifierstringобовʼязковий

Оригінал, не хеш.

client_idstring

Тільки публічні застосунки, замість Basic.

200 OK
{
  "access_token": "ahb_at_qkT9v2Nb…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "email openid profile",
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6…"
}

Відповідь

access_tokenstring

Непрозорий рядок, не JWT.

token_typestring

Bearer

expires_inint

Секунди.

scopestring

Що дозволили. Може бути вужче за запит.

id_tokenJWT

Якщо просили openid. RS256.

refresh_tokenstring

Якщо просили offline_access. Ротується.

5

id_token і профіль

ваш сервер

Підпис — ключем із /.well-known/jwks.json. Звірте iss, aud, exp, nonce. Профіль — із claims або запитом:

curl https://anihub.in.ua/api/oauth/userinfo \
  -H "Authorization: Bearer $ACCESS_TOKEN"
200 OK
{
  "sub": "12345",
  "preferred_username": "gluko",
  "name": "gluko",
  "picture": "https://anihub.in.ua/media/avatars/u12345_….webp",
  "profile": "https://anihub.in.ua/profile/gluko",
  "email": "[email protected]",
  "email_verified": true
}

Усі ендпоінти

GET/.well-known/openid-configurationDiscovery — з цього починає будь-яка бібліотека
GET/.well-known/jwks.jsonПублічні ключі для перевірки id_token
GET/api/oauth/authorizeПочаток входу (браузер користувача)
POST/api/oauth/tokenОбмін коду на токени; оновлення (ваш сервер)
GET/api/oauth/userinfoПрофіль за access-токеном
GET/api/oauth/libraryСписок аніме користувача (library:read)
PUT/api/oauth/library/{id}Створити або оновити запис (library:write)
GET/api/oauth/logoutВихід із вашого застосунку (RP-initiated logout)
POST/api/oauth/revokeВідкликати конкретний токен
POST/api/oauth/introspectПеревірка токена (конфіденційні клієнти)

Дозволи

Кожен дозвіл — окремий рядок на екрані згоди. Просіть мінімум.

openid

Обовʼязковий

Ідентифікатор користувача (sub) та id_token.

profile

Профіль

Нікнейм, аватар і посилання на профіль.

email

Пошта

Адреса електронної пошти та ознака підтвердження.

library:read

Список аніме

GET /api/oauth/library — статуси, прогрес, оцінки.

library:write

Керування списком

PUT / DELETE /api/oauth/library/{anime_id} — додати, змінити, прибрати запис.

offline_access

Постійний доступ

refresh_token, щоб не просити вхід щогодини.

Refresh-токен ротується при кожному використанні, повторне пред'явлення гасить увесь ланцюжок — не запускайте два паралельні оновлення.

Вихід

Коли користувач тисне «вийти» у вас, надішліть його на:

GET https://anihub.in.ua/api/oauth/logout
      ?id_token_hint=<id_token, який ви отримали при вході>
      &post_logout_redirect_uri=https://example.com/
      &state=<ваш state>

Відкликаємо всі токени вашого застосунку для цього користувача.

  • post_logout_redirect_uri має бути зареєстрована — незареєстровану ігноруємо й показуємо власну сторінку.
  • id_token_hint може бути протермінований. Перевіряємо підпис, не строк.
  • З акаунта AniHub користувач не виходить, і згода лишається.

Перевірка застосунку

Новий застосунок працює одразу. До перевірки — застереження на екрані згоди й ліміт у 10 користувачів; ліміт лише на нових, наявні працюють далі. Заявку подаєте в налаштуваннях.

Інше

  • Адреси повернення — побайтово. /cb і /cb/ різні.
  • https:// — усім. Публічним ще й http://127.0.0.1:порт та власна схема com.example.app:/cb. localhost не приймається — лише IP.
  • Домен належить тому, хто зареєстрував першим. Чужий і домен AniHub не приймаються; loopback спільний.
  • Назва й опис не можуть містити «AniHub» чи натякати на офіційність.
  • Секрет показується один раз — далі лише перевипуск.
  • Доступ відкликається будь-коли. Обробляйте invalid_grant як «треба увійти знову».

Готові почати?

Потрібна лише підтверджена пошта на акаунті.

Створити застосунок