はじめに
ユーザー認証を実装したい。
スマホアプリ向けのAPI作っているときにユーザー認証が必要となり、JWTが良さそうだったので採用した。
以下、注意点
- 本記事の目的はあくまでJWTの導入なので、脆弱性については触れていない。
- 入門者にわかりやすくするため、有識者には誤解される表現を使っている可能性がある。
JWTとは
「Json Web Token」の略で、URLに埋め込めるJSON文字列のこと。
電子署名によってJSONが改ざんされていないかをチェックできるため、ユーザー認証などに使うことができる。筆者は詳しく理解してないので、JWT使えばログイン機能作れる!、みたいに思っている。
JWTのしくみ
- クライアントは、認証情報(ユーザーIDとパスワード)をサーバに送る(いわゆるログイン処理)
- サーバは、データベースに接続して認証情報が正しいか確認
- サーバは、ユーザーIDと有効期限といった情報を持つJSONを秘密鍵で暗号化し、JWTの形でクライアントに返す
- 以降、クライアントは、渡されたJWTを使ってサーバと通信する(いわゆるログイン状態)
JWTの有効期限が切れたら、再び1に戻って新しくJWTを発行する必要がある。
コード
command
npm install jsonwebtoken
index.js
varexpress=require("express");varapp=express();varjwt=require("jsonwebtoken");// 秘密鍵設定、本当は環境変数使うべきapp.set("superSecret",hogehoge);varapiRoutes=express.Router();// ユーザー情報、本当はデータベースにあるのをとってくるべきvarusers=[{id:"user0",pass:"huga0"},{id:"user1",pass:"huga1"},{id:"user2",pass:"huga2"}];// クライアントから送られたIDとパスワード確認してtoken(jwt)発行// "/api/authenticate"apiRoutes.post("/authenticate",(req,res)=>{varpost_id=req.body.postId;varpost_pass=req.body.postPass;for(vari=0;i<users.length;i++){// IDとパスワードが正しかったらtoken発行if(post_id==users[i].id&&post_pass==users[i].pass){vartoken=jwt.sing(user_id,app.get("superSecret"),{algorithm:HS256,expiresIn:120});res.json({success:true,msg:"Authentication successfully finished",token:token});return;}}// IDとパスワードが正しくなかった場合res.json({success:false,msg:"Authentication failed"});});// 一度クライアントに返したtokenが改ざんされずにクライアントから送られてきたか確認apiRoutes.use((req,res,next)=>{vartoken=req.body.token;// tokenがない場合、アクセスを拒否if(!token){returnres.status(403).send({success:false,msg:"No token provided"});}// tokenが改ざんされていないかチェックjwt.verify(token,app.get(superSecret),(err,decoded)=>{// tokenが不正なものだった場合、アクセス拒否if(err){console.log(err);returnres.json({success:false,msg:"Invalid token"});}// 正しいtokenの場合、認証OKするreq.decoded;next();});});// 認証後、これ以降のURIにアクセス可能となる// "/api/private"apiRoutes.get("/private",(req,res)=>{res.json({msg:"Hello world!"});});app.use("/api",apiRoutes);
解説
jwt.sing(user_id,app.get("superSecret"),{algorithm:HS256,expiresIn:120});
- アルゴリズム
algorithm: HS256
デフォルト値がこれになっており、記述しなくても問題ない
- 有効期限
expiresIn: 120
120ミリ秒expiresIn: 3h
3時間expiresIn: 2days
2日
apiRoutes.post("/authenticate",(req,res)=>{...});apiRoutes.get("/private",(req,res)=>{...});
アクセスする際のURIは、"/authenticate"
や"/private"
ではなく、/api/authenticate
や"/api/private"
となる。
理由は、コードの一番下でapp.use("/api", apiRoutes);
としているから。
さいごに
脆弱性については今後別の記事でまとめる予定。
間違いなどあれば是非コメントにお願いします。