Перейти к содержанию

Процесс онбординга работника (для фронтов)

Шаг 1. Регистрация

  1. Компания-наниматель создаёт приглашения в систему SWAP работникам или сотрудникам компании, например, в админке. Или сразу батчем1 на несколько сотен эмэйлов работников.
Интересный факт

Это похоже на систему регистрации старого Хабрахабра, где регистрация была изначально по инвайтам и зарегистрированный пользователь (в нашем случае - компания) могла пригласить другого пользователя (в нашем случае - работника/или сотрудника компании) на портал. Когда регистрация на Хабре открылась для всех, по факту всех стал "приглашать" один пользователь - НЛО (сделано это было таким образом, чтобы не переделывать существующую логику регистрации). В нашем случае - компания - SWAP Administration, которая создаётся дата-миграцией при деплое (аналогично с наполнением датасетов и созданием главного пользователя - swap_admin).

Создать приглашение можно следующими способами:

Создать приглашение (админка)

Переходим по ссылке https://api.dev.goswap.app/admin/authentication/clientinvitation/add/ и заполняем требуемые поля. Нажимаем на кнопку SAVE

Создать приглашение (API)

Чтобы создать приглашение из API, нужно выполнить следующий запрос:

HTTP
1
2
3
4
5
6
7
8
POST /v1/auth/client-invitations/batch-create/
{
    "invited_by_company": "{company_uuid}",
    "invitations": [
        {"email": "[email protected]", "role": "WORKER"},
        {"email": "[email protected]", "role": "COMPANY_MANAGER", "prefilled_fields": {"title": "C1 Company"}},
    ]
}
  1. При создании приглашения работникам на бэкэнде автоматически регистрируются их временные2 аккаунты (со временным паролем - токеном от приглашения) и они получают письмо на свои почтовые адреса о том, что компания "SWAP Administration" (или другая - инициатор выдачи приглашений пользователям) приглашает вас зарегистрироваться в системе SWAP по уникальной ссылке.

Вот как это письмо выглядит (персональные данные замазаны): letter example

  1. Пользователь переходит по этой ссылке (или qrcode, который представляет эту же ссылку) из письма, тем самым инициируя вход в систему под временным паролем.

    https://dev.goswap.app/invite/?token=EJSrRlo2oHPeT4ynSRsAR7n1uzqZnPUqTKn9Z2SIU74&[email protected]&role=WORKER

    Внутри себя фронтэнд:

    Сначала вызывает проверку этого токена:

    HTTP
    1
    POST api.dev.goswap.app/v1/client-invitations/verify-token/EJSrRlo2oHPeT4ynSRsAR7n1uzqZnPUqTKn9Z2SIU74
    

    И если токен валиден, проверка возвращает ему email пользователя в пэйлоуде.

    Дальше фронт логинит пользователя с данными, которые пришли ему при переходе по ссылке в письме.

    Где email - query param 'email', а password - query param 'token'.

    HTTP
    1
    2
    POST api.dev.goswap.app/v1/auth/regular/login
    {"email": "[email protected]", "password": "EJSrRlo2oHPeT4ynSRsAR7n1uzqZnPUqTKn9Z2SIU74"}
    

    И помещает вернувшиеся access_token и verify_token из пэйлоуда в cookies после успешного входа в систему.

    Фронты, внимание!

    Вы выполняете этот запрос когда пользователь переходит по ссылке или qrcode из email на сайт. Проще говоря - пользователь должен быть уже залогинен в свой временный аккаунт, когда зайдёт на экран с установкой пароля. Иначе пароль не сменится! POST api.dev.goswap.app/v1/auth/regular/password/change работает только для активного (то есть залогинившегося) пользователя!

    Вопрос: а может ли залогинить пользователя бэк и вернуть хидеры авторизации в verify token?

    Ответ: Фактически это будет неправильным поведением с точки зрения логики верификации. Верификация - не производит авторизацию, она проверяет только факт валидности токена. Аутентификация и верификация - априори два разных процесса, имеющих абсолютно разную логику.

  2. Авторизовавшись (бесшовно для фронта), пользователь попадает на страницу создания нового пароля (экран 1.0). Ему остаётся только поменять временный пароль на свой и установить дату рождения, ФИО и т.д. Это два запроса:

    1. Смена пароля
    HTTP
    1
    2
    POST api.dev.goswap.app/v1/auth/regular/password/change/
    {"new_password1": "<new_password>", "new_password2": "<new_password>"}
    
    1. Обновление базовых данных аккаунта уже авторизованного пользователя
    HTTP
    1
    2
    3
    4
    5
    6
    PATCH api.dev.goswap.app/v1/auth/regular/user/
    {
        "username": "loremsupernickname",
        "full_name": "Lorem Ipsum",
        "birth_date": "1968-01-08"
    }
    

Однако фронтам стоит учесть, что созданный аккаунт временный (вы получите эту инфу в результате выполнения запроса POST core.dev.goswap.app/api/v1/auth/regular/login/ в поле "is_verified": false объекта user) и после запроса на сохранение данных запросить ещё отправку верификационного email, выполнив следующий запрос:

HTTP
1
2
POST api.dev.goswap.app/v1/auth/regular/verify-email/
{"key": "[email protected]"}

Шаг 2. Создание (открытие) клиентской сессии

Клиентская сессия - это все данные, которые мы сохраняем с разных формочек на клиенте и которые будут использованы для синхронизации между устройствами.

Сейчас объясню на примере.

Условно, начал работник заполнение профиля в телефоне, затем понял, что некоторые документы у него только на компе и решил перейти на комп. И чтобы ему не вводить всё по новой - мы сохраняем состояние его клиентской сессии в БД. Затем, когда он переходит за комп, и авторизуется в системе - мы находим открытую его сессию на телефоне с данными форм и переносим их на комп. После чего сессию на телефоне мы можем закрыть или оставить. Любые изменения в формах на фронте влекут за собой вызов следующего запроса:

HTTP
1
2
PATCH api.dev.goswap.app/v1/auth/client-sessions/{session_id}
{"filled_fields": {"<form_name>": {...updated fields }}}
Фронты, внимание!

Этот запрос полностью меняет содержимое полей (не prepend, а замена!), поэтому вы должны отправлять мне ВСЕ поля, включая неизменённые. Возможно в дальнейшем я поправлю эту логику, когда пойму, как лучше сделать.

Для создания (открытия) клиентской сессии в первую очередь нужно сгенерировать session_id.

Сгенерировать {session_id} нужно на фронтэнде, например, вот таким способом:

JavaScript
1
const session_id = window.crypto.randomBytes(48).toString("base64url");

Допустим, эта команда сгенерировала нам следующий Session ID: i4mch15iicbvhhrhpx1bohv1zvvzeidulmi

Для открытия клиентской сессии на бэкэнде нужно выполнить следующий POST запрос (не забывая прикрепить хидеры клиентского приложения):

Bash
1
2
3
4
5
6
7
8
curl https://api.dev.goswap.app/v1/auth/client-sessions/i4mch15iicbvhhrhpx1bohv1zvvzeidulmi \
    -X POST \
    -H "Content-Type: application/json" \
    -H "X-SWAP-CLIENT-APP-TOKEN: ..." \
    -H "X-SWAP-CLIENT-APP-FLAVOR: ..." \
    -H "X-SWAP-CLIENT-APP-STAGE: ..." \
    -H "X-SWAP-CLIENT-APP-VERSION: ..." \
    -d '{}'

Данные для хидеров можно посмотреть здесь: https://api.dev.goswap.app/admin/authentication/clientapplication/

Для фронтэнда берём Web, для мобильных устройств Mobile.

В ответ вернётся подобный response:

Text Only
1
2
3
4
5
6
7
8
9
{
    "id": "3f087e5f-3e07-4d31-a625-8fc1d1d597c3",  // идентификатор сессии в бд, он вам НЕ НУЖЕН
    "expired_at": "2025-10-15T08:03:36.850059Z",   // дата истечения сессии
    "created_at": "2025-10-14T08:03:36.850059Z",   // дата создания сессии
    "updated_at": "2025-10-14T08:03:36.850079Z",   // дата последнего обновления сессии
    "session_id": "aSjrlYnFhsWrOeE1y0d38VyZsq47nzFddOWWfq2HPic",  // идентификатор сессии для фронтэнда - этот идентификатор вам нужен!
    "token": "DfIsocWnQrZAHT65cJmIpZoWvH_k7hJyTBCICFDhWro",       //  уникальный токен клиентского приложения
    "time_to_live": 86400  // время жизни клиентской сессии - в секундах. По умолчанию - один день (24 часа).
}

Шаг 3. Заполнение данных профиля работника

Создаём профиль работника. В фигме это экран 1.1 на странице worker - тот, что слева от этого экрана. Прямая ссылка на него: https://www.figma.com/design/GTSngpLtUDwLxAwGL4aa7D/mobile-SWAP?node-id=2973-264941&t=AmUMGyctblFuX4VY-4

HTTP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
POST /v1/profile/worker/
{
  "first_name": "Lorem",
  "last_name": "Loremovich",
  "middle_name": "Ipsum",
  "phone_number": "+351211234567",
  "primary_email": "[email protected]",
  "gender": "Male",
  "tax_identification_number": "PT12345678",
  "country_code": "PT",
  "identification_document_type": "Passport"
}

Как это всё работает (диаграмма)

flowchart TD
    %% nodes description

    node_start((start))
    node_end((end))
    step1["
        Компания создаёт приглашение (в бэкофисе или джанговской админке сервиса core) для конкретного пользователя по его email (например [email protected]).
        При создании приглашения автоматически генерируется пригласительная ссылка,
        вида <a href='#'>https://dev.goswap.app/invite/?token=EJSrRlo2oHPeT4ynSRsAR7n1uzqZnPUqTKn9Z2SIU74&[email protected]&role=WORKER</a>
        и пользователю автоматически создаётся (если не был создан) аккаунт на бэкэнде по его email со временным паролем.
    "]
    step2["
        По этой ссылке осуществляется переход на фронтэнд.
    "]
    step3["
        На фронтэнде проверяется валидность токена <code>EJSrRlo2oHPeT4ynSRsAR7n1uzqZnPUqTKn9Z2SIU74</code>
        посредством отправки на бэкэнд запроса POST <a href='#'>https://api.dev.goswap.app/v1/client-invitations/verify-token/EJSrRlo2oHPeT4ynSRsAR7n1uzqZnPUqTKn9Z2SIU74</a>
    "]
    step4{"Токен валидный (т.е. существует и не просрочен?)"}
    step5["404 Not Found (если не существует) или 410 Gone (если просрочен)"]
    step6["
        Фронтэнд генерирует Id сессии и отправляет на бэкэнд POST запрос для открытия новой сессии (не забывая прикрепить хидеры клиентского приложения).
        POST <a href="">https://api.dev.goswap.app/v1/auth/client-sessions/{session_id}</a>
    "]
    step7["
        Когда сессия открылась, фронтэнд забирает <code>session_id</code> из вернувшегося пэйлоуда и использует его для авторизации сессии либо
        в header <code>X-SWAP-CLIENT-SESSION-ID</code>, либо в cookie <code>swap_client_session_id</code>
    "]
    step8["
        Проверить состояние сессии можно в любой момент по запросу
        GET <a href>https://api.dev.goswap.app/v1/auth/client-sessions/{session_id}</a>
        (не забываем прикрепить хидеры клиентского приложения)
        Если сессия просрочилась - вернётся 410 Gone.
        Если сессии с таким <code>session_id</code> не существует - вернётся 404 Not found.
        В противном случае вернётся следующая детализация сессии:
        {'id': '3f087e5f-3e07-4d31-a625-8fc1d1d597c3', 'expired_at': '2025-08-01T08:03:36.850059Z', 'created_at': '2025-07-31T08:03:36.850059Z', 'updated_at': '2025-07-31T08:03:36.850079Z', 'session_id': 'aSjrlYnFhsWrOeE1y0d38VyZsq47nzFddOWWfq2HPic', 'token': 'DfIsocWnQrZAHT65cJmIpZoWvH_k7hJyTBCICFDhWro', 'time_to_live': 86400}
    "]

    %% flowchart
    node_start --> step1
    step1 --> step2
    step2 --> step3
    step3 --> step4
    step4 --> |Нет| step5
    step4 --> |Да| step6
    step5 --> step1
    step6 --> step7
    step7 --> step8
    step8 --> node_end

  1. то есть выслать множество приглашений одним запросом на множество emails сразу, например

    HTTP
    1
    2
    3
    4
    5
    6
    7
    8
    POST api.dev.goswap.app/v1/auth/client-invitations/batch
    {
        "invited_by_company": "{company_uuid}",
        "invitations": [
            {"email": "[email protected]", "role": "WORKER"},
            {"email": "[email protected]", "role": "COMPANY_MANAGER", "prefilled_fields": {"title": "C1 Company"}}
        ]
    }
    
     

  2. временные - потому что все неверифицированные аккаунты чистятся определённой таской в кронтабе, условно - раз в неделю