Flask使ってAPI！認証機能を作ってみたよ　みんな見てね



#はじめに 
APIって何なのってとこから始まり、色々調べてくうちに:skull_crossbones:**Flask**:skull_crossbones:に辿り着きました。 
とりあえず触ってみようってことで私の軌跡を残そうと思います。 

IT企業に入社しましたが運用系にいるため開発は趣味として行っています。 
理解していないことも多く、間違ったことたくさんあると思うのでご指摘の程よろしくお願いします。 

Ubuntu18.04,Python3を使っています。 

#問題・やりたいこと 
- flaskを使ってAPIを実装する
- ユーザー認証後、アクセストークンを発行する

#解決策・実施したこと 

英二:baseball:はパスワードに大好きな"ゆでたまご"を設定しています。

```python
User = {
    'eiji':{
        #yudetamago
        'password':'pbkdf2:sha256:50000$j3QeNmki$6bf450823b50d5f0ee308bf8bd001d596323081ce227fb3f68612e440bf891c6'
    }
}
```

ゆでたまごはwerkzeugのgenerate_password_hashを使ってハッシュ化しています。

```linux
$python
>>> from werkzeug.security import generate_password_hash
>>> generate_password_hash('yudetamago')
'pbkdf2:sha256:50000$j3QeNmki$6bf450823b50d5f0ee308bf8bd001d596323081ce227fb3f68612e440bf891c6'

```

ユーザー情報とPOSTされた情報を照合していきます。

```python
from flask import Flask,request,make_response,jsonify
from werkzeug.security import check_password_hash
import datetime
import jwt

@api.route('/authorize',methods=['POST'])
def authorize():
    input_name = request.json['name']
    input_password = request.json['password']
    eiji_pass = User[input_name]['password']

    if not check_password_hash(eiji_pass,input_password):
        return make_response('Password is incorrect',400)
```
POSTされたデータからnameとpasswordを取り出し、nameに該当するパスワードを取り出します。
check_password_hash()ではPOSTされたパスワードと登録されたパスワードを使ってチェックします。

```python

    exp = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
    encoded = jwt.encode({'name': input_name,'exp': exp}, 'SECRET_KEY', algorithm='HS256')
    response = {'user':input_name,'token':encoded.decode('utf-8')}
    return make_response(jsonify(response),200)

```
ユーザー名とパスワードが正しかった場合、アクセストークンを発行します。
名前とシークレット値と有効時間から作ってます。

flaskを実行し、別のターミナルからcurlコマンドを実行します。

```linux
$ curl http://localhost:5000/authorize -X POST -H "Content-Type: application/json" -d '{"name":"eiji","password":"yudetamago"}'
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiZWlqaSIsImV4cCI6MTU0NzQ1NzQ3NH0.XTHMba76dec2dzlFl0QF_d4r0XFW1DFdy2tE-VfpKWE",
  "user": "eiji"
}
```
パスワードが正しければユーザー名とアクセストークンを返してくれます。

認証後、アクセストークンを使うためにlogin_required()を作ります。

```python
from flask import Flask,request,abort
import functools
import jwt


def login_required(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        header = request.headers.get('Authorization')
        _,token = header.split()
        try:
            decoded = jwt.decode(token,'SECRET_KEY',algorithms='HS256')
            username=decoded['name']
        except jwt.DecodeError:
            abort(400,message='Token is not valid.')
        except jwt.ExpiredSignatureError:
            abort(400,message='Token is expired.')
        return method(username,*args, **kwargs)
    return wrapper
```
中身はヘッダーにアクセストークンが含まれるか、有効時間内かをチェックしています。

```python
@api.route('/user',methods=['GET'])
@login_required
def user(username):
     return 'darega bandou eiji ya!' 

```

 取得したアクセストークンをヘッダーとしてリクエストします。

```linux:
curl http://localhost:5000/user -X GET -H '{"Authorization: JWT アクセストークン"}'

>>>eiji: darega bando eiji ya!

```

  
#おわりに 

こんな感じかなっていうノリで認証機能を実装してみました。
APIも機能を合わせて作ってみました。

jwtはPyJwtと明示してpip installしないとjwtというパッケージをインストールしちゃうかもよ
きをつけてね

Markdown方式でプレビューしながら書いてるとプレビュー側を編集しようとしてしまう。

・参考 

[jwtの中身を見る](https://qiita.com/leo1109/items/2e82c899891f8e771315)