# Простий OAuth2 з паролем і Bearer { #simple-oauth2-with-password-and-bearer }

Тепер продовжимо з попереднього розділу і додамо відсутні частини, щоб отримати повний потік безпеки.

## Отримайте `username` і `password` { #get-the-username-and-password }

Ми використаємо утиліти безпеки **FastAPI**, щоб отримати `username` і `password`.

OAuth2 визначає, що під час використання «потоку паролю» (який ми використовуємо) клієнт/користувач має надіслати поля `username` і `password` як дані форми.

І специфікація каже, що поля мають називатися саме так. Тому `user-name` або `email` не підійдуть.

Але не хвилюйтеся, у фронтенді ви можете відображати це так, як забажаєте, для кінцевих користувачів.

І ваші моделі бази даних можуть використовувати будь-які інші назви.

Але для *операції шляху* входу ми маємо використовувати саме ці назви, щоб бути сумісними зі специфікацією (і мати змогу, наприклад, користуватися вбудованою системою документації API).

Специфікація також вказує, що `username` і `password` мають надсилатися як дані форми (тобто без JSON).

### `scope` { #scope }

Специфікація також каже, що клієнт може надіслати інше поле форми «`scope`».

Назва поля форми — `scope` (в однині), але насправді це довга строка зі «scopes», розділеними пробілами.

Кожен «scope» — це просто строка (без пробілів).

Їх зазвичай використовують для оголошення конкретних прав доступу, наприклад:

- `users:read` або `users:write` — поширені приклади.
- `instagram_basic` використовується Facebook / Instagram.
- `https://www.googleapis.com/auth/drive` використовується Google.

/// info | Інформація

У OAuth2 «scope» — це просто строка, що оголошує конкретний потрібний дозвіл.

Не має значення, чи містить вона інші символи на кшталт `:` або чи це URL.

Ці деталі залежать від реалізації.

Для OAuth2 це просто строки.

///

## Код для отримання `username` і `password` { #code-to-get-the-username-and-password }

Тепер скористаймося утилітами, наданими **FastAPI**, щоб обробити це.

### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }

Спочатку імпортуйте `OAuth2PasswordRequestForm` і використайте його як залежність із `Depends` в *операції шляху* для `/token`:

{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}

`OAuth2PasswordRequestForm` — це клас залежності, що оголошує тіло форми з:

- `username`.
- `password`.
- Необов'язковим полем `scope` як великою строкою, складеною зі строк, розділених пробілами.
- Необов'язковим `grant_type`.

/// tip | Порада

Специфікація OAuth2 насправді вимагає поле `grant_type` із фіксованим значенням `password`, але `OAuth2PasswordRequestForm` цього не примушує.

Якщо вам потрібно це примусити, використовуйте `OAuth2PasswordRequestFormStrict` замість `OAuth2PasswordRequestForm`.

///

- Необов'язковим `client_id` (для нашого прикладу не потрібно).
- Необов'язковим `client_secret` (для нашого прикладу не потрібно).

/// info | Інформація

`OAuth2PasswordRequestForm` — не спеціальний клас для **FastAPI**, як `OAuth2PasswordBearer`.

`OAuth2PasswordBearer` робить так, що **FastAPI** знає, що це схема безпеки. Тому її додають таким чином до OpenAPI.

Але `OAuth2PasswordRequestForm` — це просто клас залежності, який ви могли б написати самі, або ви могли б напряму оголосити параметри `Form`.

Оскільки це типова задача, **FastAPI** надає його безпосередньо, щоб спростити роботу.

///

### Використовуйте дані форми { #use-the-form-data }

/// tip | Порада

Екземпляр класу залежності `OAuth2PasswordRequestForm` не матиме атрибута `scope` з довгою строкою, розділеною пробілами, натомість він матиме атрибут `scopes` зі справжнім списком строк для кожного надісланого scope.

У цьому прикладі ми не використовуємо `scopes`, але ця можливість є, якщо знадобиться.

///

Тепер отримайте дані користувача з (фальшивої) бази даних, використовуючи `username` з поля форми.

Якщо такого користувача немає, повертаємо помилку «Incorrect username or password».

Для помилки використовуємо виняток `HTTPException`:

{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}

### Перевірте пароль { #check-the-password }

На цьому етапі ми маємо дані користувача з нашої бази даних, але ще не перевірили пароль.

Спершу розмістимо ці дані в Pydantic-моделі `UserInDB`.

Ви ніколи не повинні зберігати паролі у відкритому вигляді, тож ми використаємо (фальшиву) систему хешування паролів.

Якщо паролі не збігаються, повертаємо ту саму помилку.

#### Хешування паролів { #password-hashing }

«Хешування» означає: перетворення деякого вмісту (у цьому випадку пароля) у послідовність байтів (просто строку), яка виглядає як нісенітниця.

Кожного разу, коли ви передаєте точно той самий вміст (точно той самий пароль), ви отримуєте точно ту саму «нісенітницю».

Але ви не можете перетворити цю «нісенітницю» назад у пароль.

##### Навіщо використовувати хешування паролів { #why-use-password-hashing }

Якщо вашу базу даних вкрадуть, зловмисник не матиме паролів користувачів у відкритому вигляді, лише хеші.

Тож зловмисник не зможе спробувати використати ті самі паролі в іншій системі (оскільки багато користувачів використовують той самий пароль всюди, це було б небезпечно).

{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}

#### Про `**user_dict` { #about-user-dict }

`UserInDB(**user_dict)` означає:

Передати ключі та значення з `user_dict` безпосередньо як аргументи ключ-значення, еквівалентно до:

```Python
UserInDB(
    username = user_dict["username"],
    email = user_dict["email"],
    full_name = user_dict["full_name"],
    disabled = user_dict["disabled"],
    hashed_password = user_dict["hashed_password"],
)
```

/// info | Інформація

Для повнішого пояснення `**user_dict` перегляньте [документацію для **Додаткових моделей**](../extra-models.md#about-user-in-dict).

///

## Поверніть токен { #return-the-token }

Відповідь точки входу `token` має бути об'єктом JSON.

Вона повинна містити `token_type`. У нашому випадку, оскільки ми використовуємо токени «Bearer», тип токена має бути «`bearer`».

Також має бути `access_token` зі строкою, що містить наш токен доступу.

Для цього простого прикладу ми зробимо повністю небезпечно і повернемо той самий `username` як токен.

/// tip | Порада

У наступному розділі ви побачите справді безпечну реалізацію з хешуванням паролів і токенами <abbr title="JSON Web Tokens - Токени JSON Web">JWT</abbr>.

А поки зосередьмося на конкретних деталях, які нам потрібні.

///

{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}

/// tip | Порада

Згідно зі специфікацією, ви маєте повертати JSON з `access_token` і `token_type`, як у цьому прикладі.

Це те, що ви маєте зробити самостійно у своєму коді, і переконатися, що використовуєте саме ці ключі JSON.

Це майже єдине, що ви маєте зробити правильно самі, щоб відповідати специфікації.

Решту **FastAPI** зробить за вас.

///

## Оновіть залежності { #update-the-dependencies }

Тепер оновимо наші залежності.

Ми хочемо отримати `current_user` лише якщо цей користувач активний.

Тому створимо додаткову залежність `get_current_active_user`, яка своєю чергою використовує як залежність `get_current_user`.

Обидві ці залежності просто повернуть HTTP-помилку, якщо користувача не існує або він неактивний.

Отже, у нашій кінцевій точці ми отримаємо користувача лише якщо він існує, був правильно автентифікований і є активним:

{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}

/// info | Інформація

Додатковий заголовок `WWW-Authenticate` зі значенням `Bearer`, який ми тут повертаємо, також є частиною специфікації.

Будь-який HTTP (помилка) зі статус-кодом 401 «UNAUTHORIZED» також має повертати заголовок `WWW-Authenticate`.

У випадку токенів носія (наш випадок) значенням цього заголовка має бути `Bearer`.

Фактично ви можете пропустити цей додатковий заголовок, і все одно працюватиме.

Але він наданий тут, щоб відповідати специфікаціям.

Також можуть існувати інструменти, які очікують і використовують його (зараз або в майбутньому), і це може бути корисно вам або вашим користувачам, зараз або в майбутньому.

У цьому користь стандартів...

///

## Подивіться в дії { #see-it-in-action }

Відкрийте інтерактивну документацію: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).

### Автентифікація { #authenticate }

Натисніть кнопку «Authorize».

Використайте облікові дані:

Користувач: `johndoe`

Пароль: `secret`

<img src="/img/tutorial/security/image04.png">

Після автентифікації в системі ви побачите таке:

<img src="/img/tutorial/security/image05.png">

### Отримайте власні дані користувача { #get-your-own-user-data }

Тепер використайте операцію `GET` зі шляхом `/users/me`.

Ви отримаєте дані свого користувача, наприклад:

```JSON
{
  "username": "johndoe",
  "email": "johndoe@example.com",
  "full_name": "John Doe",
  "disabled": false,
  "hashed_password": "fakehashedsecret"
}
```

<img src="/img/tutorial/security/image06.png">

Якщо натиснути значок замка й вийти з системи, а потім знову спробувати ту саму операцію, ви отримаєте помилку HTTP 401 такого вигляду:

```JSON
{
  "detail": "Not authenticated"
}
```

### Неактивний користувач { #inactive-user }

Тепер спробуйте з неактивним користувачем, автентифікуйтеся з:

Користувач: `alice`

Пароль: `secret2`

І спробуйте використати операцію `GET` зі шляхом `/users/me`.

Ви отримаєте помилку «Inactive user», наприклад:

```JSON
{
  "detail": "Inactive user"
}
```

## Підсумок { #recap }

Тепер у вас є інструменти для реалізації повної системи безпеки на основі `username` і `password` для вашого API.

Використовуючи ці інструменти, ви можете зробити систему безпеки сумісною з будь-якою базою даних і з будь-якою моделлю користувача чи даних.

Єдина відсутня деталь у тому, що наразі це ще не «безпечна» реалізація.

У наступному розділі ви побачите, як використовувати безпечну бібліотеку для хешування паролів і токени <abbr title="JSON Web Tokens - Токени JSON Web">JWT</abbr>.
