Type스크립트로 jwt를 만들기
[JWT] JSON Web Token 소개 및 구조 | VELOPERT.LOG 의 실습을 deno로 해 본다.
test_jwt.ts
import {encode as base64url_encode} from "https://deno.land/std@0.103.0/encoding/base64url.ts"
import {assertEquals} from "https://deno.land/std@0.103.0/testing/asserts.ts"
function json2Uint8Array(json: any) : Uint8Array {
return new TextEncoder().encode(JSON.stringify(json))
}
// encode header
const header = {
"typ": "JWT",
"alg": "HS256"
};
const encodedHeader = base64url_encode(json2Uint8Array(header));
assertEquals(encodedHeader, "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9")
// encode payload
const payload = {
"iss": "velopert.com",
"exp": "1485270000000",
"https://velopert.com/jwt_claims/is_admin": true,
"userId": "11028373727102",
"username": "velopert"
};
const encodedPaylod = base64url_encode(json2Uint8Array(payload));
assertEquals(encodedPaylod, "eyJpc3MiOiJ2ZWxvcGVydC5jb20iLCJleHAiOiIxNDg1MjcwMDAwMDAwIiwiaHR0cHM6Ly92ZWxvcGVydC5jb20vand0X2NsYWltcy9pc19hZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTEwMjgzNzM3MjcxMDIiLCJ1c2VybmFtZSI6InZlbG9wZXJ0In0")
// encode signiture
import {hmac as createHmac} from "https://deno.land/x/crypto@v0.10.0/hmac.ts"
const private_secret: Uint8Array = new TextEncoder().encode("secret")
const value = new TextEncoder().encode(encodedHeader + "." + encodedPaylod)
const hmacValue = createHmac('sha256', private_secret, value)
const signature = base64url_encode(hmacValue)
assertEquals(signature, "WE5fMufM0NDSVGJ8cAolXGkyB5RmYwCto1pQwDIqo2w")
console.log("JWT token: " + encodedHeader+ "." + encodedPaylod + "." + signature)
Firebase 로그인에서 JWT를 만들기
그것을 api 서버에서 private_secret로 검증해서 유효한 API call인지 검증할 수 있을 것 같다. 검증은 nginx에서 할 수 있을 듯... ( Examples (nginx.org) )
제일 좋은 것은.. Nginx에서 JWT 검증도 하고, exp 만료도 체크하고, 데이타를 까서 특정 값을 헤더로 넣어주는 것이다. API 서버에서는 헤더에서 가져와서 바로 사용하면 된다.
https://mostafa-asg.github.io/post/nginx-authentication-with-jwt/ 아마도 nginx 를 새로 빌드를 해야할 듯하다.
https://firebase.google.com/docs/functions/callable?hl=ko#java 참고..
# 라이브러리 설치
$ npm install -g firebase-tools
# firebase 로그인
$ firebase login
# 프로젝트 생성
$ firebase init
# 프로젝트 배포
$ firebase depoly
base64url은 base64의 문자중 3개를 (+ / =) 다른 것으로 교체하는 간단한 (jwt.io 방문해서 스펙을 확인)
npm install base64url
Firebase functions에서 JWT를 생성하는 코드, exp를 24시간을 준다.
Index.js
const functions = require("firebase-functions");
const base64url = require('base64url');
const crypto = require('crypto');
const PRIVATE_SECRET = "blabla";
const MAX_EXPIRE_SECONDS = 60 * 60 * 24; //24 hours
function generate_hs256_jwt(claims) {
var header = { typ: "JWT", alg: "HS256" };
var s = [header, claims].map(JSON.stringify)
.map(v=>base64url(v))
.join('.');
var h = crypto.createHmac('sha256', PRIVATE_SECRET);
return s + '.' + base64url(h.update(s).digest())
}
exports.createJwtToken = functions.https.onCall((data, context) => {
// Message text passed from the client.
const text = data.text;
// Checking attribute.
if (!(typeof text === 'string') || text.length === 0) {
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('invalid-argument', 'The function must be called with ' +
'one arguments "text" containing the message text to add.');
}
// Checking that the user is authenticated.
if (!context.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
// Authentication / user information is automatically added to the request.
const fuid = context.auth.uid;
var expireSeconds = MAX_EXPIRE_SECONDS;
const exp = Math.floor(Date.now()/1000) + expireSeconds;
var claims = {
"iss": "lt",
"exp": exp,
"fuid": fuid
};
try {
const token = generate_hs256_jwt(claims);
return {
code: 0,
result: {
"exp": exp,
"token": token
}
};
} catch(error) {
console.log(error);
return {
code: 500,
message: "generate_hs256_jwt() failed"
};
}
});
배포
firebase deploy --only functions:createJwtToken
안드로이드에서 호출하기는 https://dajkim76.tistory.com/537 참고