Python Fast API セキュリティまとめてみた

# Security Intro
- セキュリティ、認証、認可を扱う方法はたくさんあります。そして通常、それは複雑で「難しい」トピックです。
- 多くのフレームワークやシステムでは、セキュリティと認証を処理するだけでも大量の労力とコードを必要とします（多くの場合、書かれたコードの50％以上になることもあります）。
- FastAPIは、すべてのセキュリティ仕様を勉強して学ぶことなく、標準的な方法で簡単に、迅速に、セキュリティを扱うのに役立つツールをいくつか提供しています。
- しかし、最初に、いくつかの小さな概念を確認してみましょう。

## 忙しい人向けに
- もしあなたがこれらの用語のどれにも興味がなく、今すぐにユーザ名とパスワードに基づいた認証でセキュリティを追加する必要があるのであれば、次の章まで読み飛ばしてください。

## OAuth2
- OAuth2 は、認証と認可を処理するためのいくつかの方法を定義した仕様です。これは非常に広範な仕様で、いくつかの複雑なユースケースをカバーしています。
- その中には、「サードパーティ」を使って認証する方法も含まれています。Facebook, Google, Twitter, GitHubでログインしているシステムは、その下にあるものを使っています。

## OAuth 1
- OAuth1というものがありましたが、これはOAuth2とは大きく異なり、通信をどのように暗号化するかという仕様が直接含まれていて、より複雑なものでした。現在ではあまり普及していませんし、使われていません。
- OAuth2は通信を暗号化する方法を指定しておらず、アプリケーションがHTTPSで提供されることを期待しています。
- デプロイメントについてのセクションでは、TraefikとLet's Encryptを使って無料でHTTPSを設定する方法を紹介しています。


## OpenID Connect
- OpenID Connect は OAuth2 をベースにした別の仕様です。
- これはOAuth2を拡張したもので、OAuth2の中では比較的曖昧なものをいくつか指定して、より相互運用性を高めようとしています。
- 例えば、GoogleのログインはOpenID Connectを使用しています（その下のOAuth2を使用しています）。
- しかし、FacebookのログインはOpenID Connectをサポートしていません。それはOAuth2の独自のフレーバーを持っています。


## OpenID (not "OpenID Connect")
- OpenIDという仕様もありました。これはOpenID Connectと同じことを解決しようとしていましたが、OAuth2をベースにしていませんでした。現在ではあまり普及していないし、使われていません。


## OpenAPI
- OpenAPI（以前はSwaggerとして知られていました）は、APIを構築するためのオープンな仕様です（現在はLinux Foundationの一部となっています）。FastAPIはOpenAPIをベースにしています。そのため、複数の自動対話型ドキュメントインターフェイスやコード生成などが可能になっています。
- OpenAPIには、複数のセキュリティ「スキーム」を定義する方法があります。それらを利用することで、これらの対話型ドキュメントシステムをはじめとする標準ベースのすべてのツールを活用することができます。
- OpenAPIは以下のセキュリティスキームを定義します。
    - apiKey: 
        - query parameter.
        - header.
        - cookie.
    - http: 標準的なHTTP認証システム。
        - bearer: ヘッダ 認証に Bearer とトークンを加えた値。これは OAuth2 から継承されています。
        - HTTP Basic authentication.
    - oauth2: セキュリティを扱うためのすべてのOAuth2の方法(「フロー」と呼ばれる)。
    -  これらのフローのいくつかは、OAuth2.0認証プロバイダ（Google、Facebook、 Twitter、GitHubなど）を構築するのに適しています。
    - implicit
    - クライアント認証情報
    - オーソライズコード
しかし、同じアプリケーションで認証を直接処理するために完璧に使用できる特定の「フロー」があります。
password: 次の章では、この例を取り上げます。
openIdConnect: OAuth2 認証データを自動的に発見する方法を定義する方法があります。
この自動検出は OpenID Connect 仕様で定義されているものです。


## Fast API Utilities
- FastAPI は fastapi.security モジュールで、これらのセキュリティ メカニズムの使用を簡単にするために、これらのセキュリティ スキームのそれぞれにいくつかのツールを提供しています。
- 次の章では、FastAPI が提供するツールを使用して API にセキュリティを追加する方法を見ていきます。また、対話型ドキュメントシステムに自動的に統合される方法も見てみましょう。


# Security - First Steps
- あるバックエンドAPIがあるとしましょう。
- そして、フロントエンドが別のドメイン、または同じドメインの別のパス (またはモバイルアプリケーション) にあるとします。
- そして、フロントエンドがユーザー名とパスワードを使ってバックエンドと認証する方法が欲しいとします。
- OAuth2を使用してFastAPIでそれを構築することができます。
- 必要な情報を見つけるために長い仕様書を読む時間を節約しましょう。
- FastAPIが提供するツールを使ってセキュリティを処理してみましょう。


## How it looks
- まずはコードを使って様子を見て、何が起こっているのかを理解するために戻ってきましょう。


```
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}
```


- あなたはすでにピカピカの新しい "Authorize "ボタンを持っています。
- そして、あなたのパス操作には右上隅にクリックできる小さなロックが付いています。
- そして、それをクリックすると、ユーザ名とパスワード（その他のオプションフィールド）を入力するための小さな認証フォームが表示されます。
- これはもちろん最終的なユーザーのためのフロントエンドではありませんが、すべてのAPIをインタラクティブにドキュメント化するための素晴らしい自動ツールです。
- これはフロントエンドチームが使用することができます (これはあなた自身が使用することもできます)。
- サードパーティのアプリケーションやシステムで使うこともできます。
- また、同じアプリケーションをデバッグ、チェック、テストするために、自分自身で使用することもできます。

## The password flow
- ここで少し話を戻して、これが何なのかを理解してみましょう。
- パスワードの「フロー」は、OAuth2で定義されているセキュリティと認証を扱うための方法（「フロー」）の一つです。
- OAuth2は、バックエンドやAPIがユーザを認証するサーバから独立したものになるように設計されています。しかし、この場合、同じFastAPIアプリケーションがAPIと認証を処理します。では、その単純化した観点からおさらいしてみましょう。
1. ユーザーはフロントエンドでユーザー名とパスワードを入力して Enter キーを押します。
2. フロントエンド (ユーザーのブラウザで実行されている) は、そのユーザー名とパスワードを API の特定の URL (tokenUrl="token" で宣言) に送信します。
3. API はそのユーザ名とパスワードをチェックし、"token" で応答します (これはまだ実装していません)。
4. token" は単なる文字列で、後でこのユーザを確認するために使用することができます。
5. 通常、トークンは時間が経つと失効するように設定されています。そのため、ユーザーは後日、再度ログインしなければなりません。
6. そして、トークンが盗まれた場合のリスクは少ない。永久キーのように永遠に機能するものではありません（ほとんどの場合）。
7. フロントエンドはそのトークンを一時的にどこかに保存します。ユーザーはフロントエンドでクリックして、フロントエンドのウェブアプリの別のセクションに移動します。
8. フロントエンドはAPIからさらにデータを取得する必要があります。しかし、その特定のエンドポイントの認証が必要です。
9. そこで、APIで認証するために、ヘッダのAuthorizationにBearerの値とトークンを加えたものを送信します。トークンにfoobarが含まれている場合、Authorizationヘッダーの内容は次のようになります。

## FastAPI's OAuth2PasswordBearer
- FastAPI は、これらのセキュリティ機能を実装するために、抽象度の異なる複数のツールを提供します。この例では、ベアラートークンを使用してパスワードフローで OAuth2 を使用します
- OAuth2PasswordBearer クラスを使用します。
- OAuth2PasswordBearer クラスのインスタンスを作成する際に tokenUrl パラメータを渡します。
- このパラメータには、クライアント (ユーザのブラウザで実行しているフロントエンド) がトークンを取得するためにユーザ名とパスワードを送信する際に使用する URL を指定します。
- ここで tokenUrl="token" は、まだ作成していない相対 URL のトークンを指しています。相対URLなので、./tokenと同じです。
- 相対 URL を使用しているので、API が https://example.com/ にある場合は https://example.com/token を参照します。しかし、APIが https://example.com/api/v1/ にある場合は https://example.com/api/v1/token を参照します。
- 相対 URL を使用することは、Behind a Proxy のような高度なユースケースでもアプリケーションが動作し続けることを確認するために重要です。
- このパラメータはエンドポイント/パス操作を作成しませんが、クライアントがトークンを取得するために使用する URL /token を宣言します。この情報は OpenAPI や対話型 API ドキュメントシステムで使用されます。実際のパス操作もすぐに作成します。
- oauth2_scheme変数はOAuth2PasswordBearerのインスタンスですが、「呼び出し可能」でもあります。つまり
```
oauth2_scheme(some, parameters)
```
として呼び出し可能で、Dependsを使って呼び出すことが可能です。


## What it does
- Authorizationヘッダーのリクエストを探し、値がBearerと何らかのトークンを含んでいるかどうかをチェックし、そのトークンをstrとして返します。
- Authorization ヘッダを見なかったり、値が Bearer トークンを持っていなかったりすると、直接 401 ステータスコードエラー (UNAUTHORIZED) で応答します。
- トークンが存在するかどうかをチェックしてエラーを返す必要もありません。あなたの関数が実行された場合、そのトークンに str が含まれていることを確認することができます。


# Get Current User
前章では、（依存性注入システムをベースとした）セキュリティシステムが、パス操作関数にトークンをstrとして与えていました。でも、それではまだ使い物になりません。今のユーザー情報を取得できるようにしましょう


## Create a user model
- まず、Pydanticのユーザーモデルを作成してみましょう。
- Pydanticを使ってボディを宣言するのと同じように、他の場所でも使えます。


```
from typing import Optional

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user
```


## Create a get_current_user dependency
- 依存関係 get_current_user を作成してみましょう。依存関係はサブ依存関係を持つことができることを覚えていますか？
- get_current_userは、以前に作成したのと同じoauth2_schemeを持つ依存関係を持つことになります。
- 前にパス操作で直接行っていたのと同じように、新しい依存関係のget_current_userは、サブ依存関係のoauth2_schemeからstrとしてトークンを受け取ります。
- get_current_userは、我々が作成した（偽の）ユーティリティ関数を使用し、トークンをstrとして受け取り、Pydantic Userモデルを返します。
## Inject the current user
- これで、パス操作でget_current_userと同じDependsを使うことができるようになりました。
- current_userの型をPydanticモデルのUserとして宣言していることに注目してください。
- これは関数内での補完と型のチェックに役立ちます。

## Other models
- これで、パス操作機能で現在のユーザーを直接取得し、Dependency Injectionレベルでセキュリティの仕組みをDependsを使って扱えるようになりました。
- そして、セキュリティ要件のために任意のモデルやデータ（この場合はPydanticモデルのUser）を使用することができます。
- しかし、いくつかの特定のデータモデル、クラス、型を使用することに制限されることはありません。
- IDとEメールを持っていて、モデルにユーザー名を持たないようにしたいと思いますか？もちろん、これらの同じツールを使用することができます。
- trだけにしたいですか？あるいは単にdictだけにしたいですか? あるいはデータベースクラスのモデルインスタンスを直接取得したいですか？すべて同じように動作します。
- 実際には、アプリケーションにログインするユーザーではなく、ロボット、ボット、または他のシステムが、ただのアクセストークンを持っていますか？繰り返しになりますが、すべて同じように動作します。
- アプリケーションに必要なモデル、クラス、データベースを使用してください。FastAPIは依存性インジェクションシステムでカバーしています。
## Code size
- この例は冗長に見えるかもしれません。同じファイルにセキュリティ、データモデルのユーティリティ関数、パス操作が混在していることを覚えておいてください。しかし、ここが重要なポイントです。
- セキュリティと依存性注入のものは一度書かれています。そして、それを好きなだけ複雑にすることができます。それでも、一度だけ、一箇所に書いておけばいいのです。すべての柔軟性を備えています。
- しかし、同じセキュリティシステムを使って何千ものエンドポイント (パス操作) を持つことができます。そして、そのすべてのエンドポイント（またはその一部）が、これらの依存関係や、あなたが作成した他の依存関係を再利用するという利点を利用することができます。
- そして、これらの数千のパス操作のすべては、3行のように小さくすることができます。
```
from typing import Optional

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user
```
## まとめ
- これでパス操作機能で現在のユーザーを直接取得できるようになりました。これですでに半分は達成しています。あとはユーザー/クライアントが実際にユーザー名とパスワードを送信するためのパス操作を追加するだけです。
- これは次のステップになります。
# Simple OAuth2 with Password and Bearer
- 前の章から構築し、足りない部分を追加して完全なセキュリティフローを構築してみましょう。
```
from typing import Optional

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": "fakehashedsecret",
        "disabled": False,
    },
    "alice": {
        "username": "alice",
        "full_name": "Alice Wonderson",
        "email": "alice@example.com",
        "hashed_password": "fakehashedsecret2",
        "disabled": True,
    },
}

app = FastAPI()


def fake_hash_password(password: str):
    return "fakehashed" + password


oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None


class UserInDB(User):
    hashed_password: str


def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)


def fake_decode_token(token):
    # This doesn't provide any security at all
    # Check the next version
    user = get_user(fake_users_db, token)
    return user


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    return user


async def get_current_active_user(current_user: User = Depends(get_current_user)):
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user


@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user_dict = fake_users_db.get(form_data.username)
    if not user_dict:
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    user = UserInDB(**user_dict)
    hashed_password = fake_hash_password(form_data.password)
    if not hashed_password == user.hashed_password:
        raise HTTPException(status_code=400, detail="Incorrect username or password")

    return {"access_token": user.username, "token_type": "bearer"}


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
    return current_user
```
## Get the username and password
- ユーザー名とパスワードを取得するためにFastAPIセキュリティユーティリティを使用します。
- OAuth2では、（私たちが使用している）"パスワードフロー "を使用する場合、クライアント/ユーザーはフォームデータとしてユーザー名とパスワードのフィールドを送信しなければならないと規定されています。そして、この仕様では、フィールドはそのような名前でなければならないとされています。そのため、ユーザ名やメールは使えません。
- しかし、心配しないでください。フロントエンドの最終ユーザーには、あなたが望むように表示することができます。そして、データベースモデルはあなたが望む他の名前を使うことができます。
- しかし、ログインパスの操作のためには、仕様との互換性を保つためにこれらの名前を使用する必要があります (そして、例えば、統合された API ドキュメントシステムを使用できるようにするために)。また、仕様では、ユーザー名とパスワードはフォームデータとして送信しなければならないとされています(なので、ここではJSONは使用しません)。
### scope
- 仕様書には、クライアントは別のフォームフィールド「スコープ」を送信することもできると書かれています。
- フォームフィールド名はスコープ(単数形)ですが、実際にはスペースで区切られた「スコープ」を持つ長い文字列です。それぞれの「スコープ」はただの文字列です(スペースなし)。
- これらは通常、例えば特定のセキュリティ権限を宣言するために使用されます。
    - users:read や users:write が一般的な例です。
    - instagram_basicはFacebook / Instagramで使用されています。
    - https://www.googleapis.com/auth/drive は Google が使用しています。
- OAuth2では、"scope "は特定のパーミッションを宣言する文字列にすぎません。それが : のような他の文字を含んでいても、URLであっても問題ではありません。これらの詳細は実装固有のものです。OAuth2では、これらは単なる文字列です。
## Code to get the username and password
では、FastAPIが提供するユーティリティを使って処理してみましょう。
OAuth2PasswordRequestFormを使用します。
まず、OAuth2PasswordRequestFormをインポートし、/tokenのパス操作でDependsを使って依存関係として使用します。
- OAuth2PasswordRequestFormは、フォームボディを宣言するクラス依存関係です。
    - The username.
    - The password.
    - An optional scope field as a big string, composed of strings separated by spaces.
    - An optional grant_type.
    - An optional client_id (we don't need it for our example).
    - An optional client_secret (we don't need it for our example).

を宣言します
- OAuth2仕様では、実際にはパスワードの固定値を持つgrant_typeというフィールドが必要ですが、OAuth2PasswordRequestFormはそれを強制していません。
- もし強制する必要がある場合は、OAuth2PasswordRequestFormの代わりにOAuth2PasswordRequestFormStrictを使用してください。
- OAuth2PasswordRequestFormはOAuth2PasswordBearerのようにFastAPI用の特別なクラスではありません。
- OAuth2PasswordBearerはFastAPIにセキュリティスキームであることを認識させます。そのようにOpenAPIに追加されています。
- しかし、OAuth2PasswordRequestFormはクラスの依存関係にすぎず、自分で書くこともできますし、Formのパラメータを直接宣言することもできます。これは一般的なユースケースなので、簡単にするために FastAPI によって直接提供されています。
## Use the form data
依存クラス OAuth2PasswordRequestForm のインスタンスは、スペースで区切られた長い文字列を持つ属性スコープを持たず、代わりに、送信された各スコープの実際の文字列リストを持つ scopes 属性を持ちます。
この例ではスコープを使用していませんが、必要であれば機能はそこにあります。
- さて、フォームフィールドのユーザー名を使って、(偽の)データベースからユーザーデータを取得します。
- そのようなユーザがいない場合は、"不正なユーザ名またはパスワード "というエラーを返します。
- このエラーにはHTTPExceptionという例外を使用します。
## Check the password
- この時点では、データベースからユーザーデータを取得していますが、パスワードを確認していません。まずはそのデータをPydantic UserInDBモデルに入れてみましょう。平文のパスワードを保存してはいけないので、（偽の）パスワードハッシュシステムを使用します。パスワードが一致しない場合は、同じエラーを返します。
### パスワードのハッシュ化
"ハッシュ化 "とは次のような意味です: あるコンテンツ(この場合、パスワード)を、ジベリッシュのようなバイト列(単なる文字列)に変換することです。
### なぜパスワードハッシングを使うのか¶ (Why use PASSWORD HASHING)
データベースが盗まれた場合、盗人はユーザの平文パスワードを持っておらず、ハッシュのみを持っています。だから、泥棒は別のシステムでそれらの同じパスワードを使用しようとすることはできません（多くのユーザーがどこでも同じパスワードを使用しているので、これは危険でしょう）。
## Update the dependencies
- では、依存関係を更新していきます。
- このユーザがアクティブな場合にのみ、current_userを取得したいのです。
- そこで、get_current_active_userという追加の依存関係を作成し、 get_current_userを依存関係として使用します。
- これらの依存関係はどちらも、ユーザーが存在しない場合やアクティブでない場合にHTTPエラーを返すだけです。
- つまり、このエンドポイントでは、ユーザーが存在し、正しく認証され、アクティブな場合にのみユーザーを取得します。
## まとめ
- これで、API用のユーザ名とパスワードに基づいた完全なセキュリティシステムを実装するためのツールを手に入れることができます。
- これらのツールを使用することで、セキュリティシステムを任意のデータベース、任意のユーザーやデータモデルと互換性のあるものにすることができます。
- 唯一欠けているのは、実際にはまだ「安全」ではないということです。次の章では、セキュアなパスワードハッシュライブラリと JWT トークンの使い方を見ていきましょう。
# OAuth2 with Password (and hashing), Bearer with JWT tokens
- これで、セキュリティの流れがすべてわかったので、JWT トークンとセキュアなパスワードハッシュを使って、実際にアプリケーションをセキュアにしてみましょう。
- このコードは、アプリケーションで実際に使用できるもので、パスワードハッシュをデータベースに保存しておくなどの工夫がされています。前の章で残したところから始めて、インクリメントしていきます。
## About JWT
- WTとは、「JSON Web Tokens」という意味です。JSONオブジェクトをスペースを入れずに長く密集した文字列でコード化するための規格です。これは次のようになります。
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
- 暗号化されていないので、誰でも内容から情報を復元することができます。しかし、それは署名されているので、自分が発行したトークンを受け取ったときに実際に発行したことを確認することができます。
- そうすれば、例えば1週間の有効期限を持つトークンを作ることができます。そうすれば、次の日にユーザーがトークンを持って戻ってきたときに、そのユーザーがまだシステムにログインしていることがわかります。
- 1週間後にはトークンの有効期限が切れてしまい、ユーザーは認証されず、新しいトークンを取得するために再度サインインしなければなりません。また、ユーザー（または第三者）が有効期限を変更するためにトークンを変更しようとした場合、署名が一致しないため、それを発見することができます。
- JWT トークンで遊んでみたい方は https://jwt.io をチェックしてみてください。
## Password hashing
- "ハッシュ化 "とは、あるコンテンツ(この場合はパスワード)をジベリッシュに見えるバイト列(単なる文字列)に変換することを意味します。全く同じ内容(全く同じパスワード)を渡すと、いつでも全く同じギバリングを得ることができます。
- しかし、そのギバリングからパスワードに戻す変換はできません。
## Why use password hashing
- データベースが盗まれた場合、盗人はユーザの平文パスワードを持っておらず、ハッシュのみを持っています。
- そのため、泥棒は別のシステムでそのパスワードを使おうとすることはできません（多くのユーザがどこでも同じパスワードを使っているので、これは危険です）。
## Install passlib
- PassLib はパスワードハッシュを扱うための優れた Python パッケージです。
- 多くの安全なハッシュアルゴリズムをサポートしており、それらを扱うためのユーティリティも用意されています。おすすめのアルゴリズムは「Bcrypt」です。なので、PassLibをBcryptと一緒にインストールしましょう。
- passlib を使えば、Django や Flask セキュリティプラグイン、その他多くのもので作成されたパスワードを読み込めるように設定することもできます。
- そのため、例えば、データベース内のDjangoアプリケーションからFastAPIアプリケーションと同じデータを共有することができます。あるいは、同じデータベースを使って Django アプリケーションを徐々に移行することもできます。
- そして、ユーザーは Django アプリからも FastAPI アプリからも、同時にログインすることができるようになります。
## Hash and verify the passwords
- passlibから必要なツールをインポートします。
- PassLibの「Context」を作成します。これがパスワードのハッシュ化と検証に使われるものです。
- PassLib Contextには、検証を可能にするために非推奨の古いものだけを含む、異なるハッシュアルゴリズムを使用する機能もあります。
- 例えば、別のシステム (Django のような) で生成されたパスワードを読み込んで検証し、新しいパスワードは Bcrypt のような別のアルゴリズムでハッシュ化することができます。そして、それらすべてと同時に互換性があります。
- ユーザから送られてきたパスワードをハッシュ化するユーティリティ関数を作成します。
- また、受信したパスワードが保存されているハッシュと一致するかどうかを検証するための別のユーティリティを作成します。そして、ユーザを認証して返すための別の関数を作成します

後日加筆修正します。