ã»ãã·ã§ã³ã¹ãã¬ã¼ã¸ãªãã§ãã°ã¤ã³ã»ãã·ã§ã³ãç¶æããä»çµã¿ãä½ã£ãã®ã§ãç°¡åã«ç´¹ä»ãã¾ãã
å æ¥oidc-authã¨ããHonoã®ããã«ã¦ã§ã¢ãå®è£ ãã¦3rd-party middlewareã¨ãã¦æ¡ç¨ãã¦ããã ãã¾ãããããã¯å¤é¨IDãããã¤ãã¼ã§èªè¨¼ãè¡ãªããèªåçºè¡ããJWTãæ¯ãªã¯ã¨ã¹ãæ¤è¨¼ãããã¨ã§ããµã¼ãå´ã§ã»ãã·ã§ã³IDãè¨é²ãããã¨ãªããã°ã¤ã³ã»ãã·ã§ã³ãç¶æãããã®ã§ãã
ãã®ã»ãã·ã§ã³ã¹ãã¬ã¼ã¸ä¸è¦ã¨ããç¹å¾´ã¯CDNã¨ãã¸ã¨è¦ªåæ§ãé«ãããã¨ãã°Cloudflare Pagesã§æä¾ããéçã³ã³ãã³ãã«Googleèªè¨¼ãã¤ãããã¨ãã£ããã¨ãã¨ãã¸ã®CPUã ãã§å®ç¾ã§ãã¾ããå ãã¦ãHonoã®ãã¼ã¿ããªãã£ã®ãããã§Deno Deployã§ãåãä»çµã¿ã使ããããã¾ãã
å人çã«ã¯å®ç¨æ§ã¨ã»ãã¥ãªãã£ã両ç«ããé¢ç½ããã®ãä½ããã¨èãã¦ãã¾ãããã»ãã¥ãªãã£é¢ã§ä¸å®ãæãã人ãããã¨æãã®ã§è§£èª¬è¨äºãæ¸ãã¦ã¿ã¾ãã
ã¨ãã¸ã³ã³ãã¥ã¼ãã£ã³ã°ã¨ã¯
æ¬é¡ã«å ¥ãåã«ãã¨ãã¸ã³ã³ãã¥ã¼ãã£ã³ã°ã«ã¤ãã¦ç°¡åã«ç´¹ä»ãã¾ãã
Webã®æèã§ã¨ãã¸ã³ã³ãã¥ã¼ãã£ã³ã°ã¨è¨ã£ãå ´åãããçæéã ãåããããªå¦çãCDNã¨ãã¸ãµã¼ããªã©ã¦ã¼ã¶ã¼ã®è¿æã§åä½ãããä»çµã¿ãæãã¨æãã¾ããCloudflare WorkersãLambda@EdgeãVercelãªã©ããã®ä»£è¡¨æ ¼ã¨è¨ããã§ãããã
ããããç°å¢ã®åä½ç°å¢ã¨ãã¦JavaScriptã¨ã³ã¸ã³ãå¤ãæ¡ç¨ããã¦ãããããã¨ãã¸ä¸ã§åããã¹ã¯ãªããã¯åºæ¬çã«JavaScriptãTypeScriptã§è¨è¿°ãããã¨ã«ãªãã¾ã1ã
ã¦ã¼ã¹ã±ã¼ã¹ã¨ãã¦ã¯ç°¡åãªå¦çãåæã¨ãã¦ãããã¨ãå¤ããã©ã®ç°å¢ã§ãå®è¡æéã®ä¸éã¯å³ããã§ããä¾ãã°Cloudflare Workersã¯å®è¡æéã®ä¸éã50ms2ã§ãã
ããå¤ãææ¸ã§ããã@yusukebeããã®ãCDNã®ã¨ãã¸ã§å®è¡ããç³»ãé¢ç½ãããèªãã¨èæ¯ãã¦ã¼ã¹ã±ã¼ã¹ããã詳ããçè§£ã§ããã¨æãã¾ãã
ãã®ã¨ãã¸ã³ã³ãã¥ã¼ãã£ã³ã°ã§ãããç¹ã«Webããã³ãã¨ã³ãçéã®æ¹ã ã«æ¯æããã¦ããå°è±¡ãããã¾ããç§ã¯é夿¼¢ãªã®ã§æ³åã«ãªãã¾ãããå©ç¨è¨èªã®è¦ªåæ§ã¨å®éã®ãã¼ãºã¨ä¸¡æ¹ãåã¿åã£ã¦ããã¨ãããã¨ãªã®ã§ããããç¹ã«ãã¨ãã¸ã§SSRããçµæãä¸å®æéãã£ãã·ã¥ãã¦ãããããªä½¿ãæ¹ã¯ã¡ãªããã大ãããã§ãã
Honoã®ç´¹ä»ã¨oidc-authã®å©ç¨ä¾
Honoã¯@yusukebeãããä½ããã¦ããTypeScript製ã®Webã¢ããªã±ã¼ã·ã§ã³ãã¬ã¼ã ã¯ã¼ã¯ã§ãã
Honoã®ç¹å¾´ãä¸è¨ã§èª¬æããã¨ããäºæ¥è ãã¨ã®å·®åã大ããã¨ãã¸å¦çã«ãã¼ã¿ããªãã£ãæä¾ããã¨ãã¸ç¨ãã¬ã¼ã ã¯ã¼ã¯ãã¨ãªãã¾ã3ãå社ã®ã¨ãã¸ã³ã³ãã¥ã¼ãã£ã³ã°ç°å¢ã¯åºæ¬çã«JavaScript/TypeScriptã§è¨è¿°ãããã®ãå¤ãã®ã§ãããããããã®å·®åãããªã大ãããè¤æ°ç¤¾ã®ç°å¢ã§åå 容ã®å¦çãæ¸ãã ãã§ãæå¤ã¨è¦å´ããå°è±¡ã§ããHonoã§æ¸ããã¨ã§å°ãã夿´ã§å¥ã®ç°å¢ã§ãåãããã¨ãã§ãã¾ãã
è«ãã証æ ã§ãä»åç§ã使ããoidc-authã使ã£ã¦éçãã¼ã¸ã«OpenID Connectã§èªè¨¼ã»èªå¯ãã¤ããã³ã¼ããè¦ã¦ã¿ã¾ããããCloudflare Pagesã®å ´åã¯ä¸è¨ã®ããã«ãªãã¾ãã
import { Hono } from 'hono' import { oidcAuthMiddleware, getAuth } from '@hono/oidc-auth' const app = new Hono() app.use('*', oidcAuthMiddleware()) app.use('*', async (c, next) => { // Authorize user with email address const auth = await getAuth(c) if (!auth?.email.endsWith('@gmail.com')) { return c.text('Unauthorized', 401) } await next() }) app.get('*', async (c) => { const response = await c.env.ASSETS.fetch(c.req.raw); // clone the response to return a response with modifiable headers const newResponse = new Response(response.body, response) return newResponse }); export default app
èªè¨¼ç¨ã®URLãã¯ã©ã¤ã¢ã³ãIDãã·ã¼ã¯ã¬ãããªã©ã¯ç°å¢å¤æ°çµç±ã§æ¸¡ãã¦ãã¾ããä¸è¨ã®å ´åã§ããã°Googleèªè¨¼ãçµç±ãã¦@gmail.comã®ã¢ãã¬ã¹ã ã£ãå ´åã ãéçãã¼ã¸ãé²è¦§ã§ãã¾ãã
次ã«Deno Deployçãè¦ã¦ã¿ã¾ãããã
import { Hono } from 'npm:hono' import { serveStatic } from 'npm:hono/deno' import { oidcAuthMiddleware, getAuth } from 'npm:@hono/oidc-auth'; const app = new Hono() app.use('*', oidcAuthMiddleware()) app.use('*', async (c, next) => { // Authorize user with email address const auth = await getAuth(c) if (!auth?.email.endsWith('@gmail.com')) { return c.text('Unauthorized', 401) } await next() }) app.use('*', serveStatic({ root: 'public/' })) Deno.serve(app.fetch)
ç°ãªãç°å¢ã§ä¼¼ãã³ã¼ããåããã¨ããããã¨æãã¾ãã
å®éã®æåãã½ã¼ã¹ã³ã¼ãã確èªãããæ¹ã¯ä¸è¨ãªã³ã¯ãã試ãã¦ã¿ã¦ãã ããã
ãèªèº«ã§åä½ç¢ºèªãããå ´åãç°å¢å¤æ°ãæä½5ã¤è¨å®ããä¸ã§IDãããã¤ãå´ã«ã³ã¼ã«ããã¯URLãç»é²ããå¿ è¦ãããã¾ãã詳細ã¯@hono/oidc-authãã確èªãã ããã
JWTã«ããã»ãã·ã§ã³ç®¡çã®å®è£
ããããæ¬é¡ã§ããoidc-authã®å®è£ ãç´¹ä»ãã¾ãã
ã·ã¼ã±ã³ã¹å³ãè¦ãã¨å°ãè¤éã«è¦ãã¾ãããIDãããã¤ãããIDãã¼ã¯ã³ãåãåºãã¨ããã¾ã§ã¯æ®éã®OpenID Connectã®èªè¨¼ããã¼ã§ãã
ãã®å¾ãåãåºããIDãã¼ã¯ã³ãå ã«èªåã§JWTãä½ãã¾ãããã®JWTã«ã¯ã¦ã¼ã¶ã¼ID4ãeã¡ã¼ã«ã¢ãã¬ã¹ããªãã¬ãã·ã¥ãã¼ã¯ã³ããªãã¬ãã·ã¥æéï¼ããã©ã«ã15åï¼ã¨ã»ãã·ã§ã³æéï¼ããã©ã«ã1æ¥ï¼ãå«ã¾ãã¦ãããHS256ã§ç½²åãã¦ãã¾ãã
ãã®JWTãSet-Cookieãããã§ãã©ã¦ã¶ã«è¿ãããã©ã¦ã¶ããéä¿¡ãããJWTãæ¯ãªã¯ã¨ã¹ãæ¤è¨¼ãã¾ããJWTã®ç½²åæ¤è¨¼ããã¹ãã¦JWTå ã®ã»ãã·ã§ã³æéãçµéãã¦ããªããã°ã»ãã·ã§ã³ãæå¹ã¨ãããã¨ã«ãªãã¾ãã
ä¸è¨ã®èª¬æãããããããã»ãã·ã§ã³ã¹ãã¬ã¼ã¸ã使ã£ã¦ããªãã®ãããããã¨æãã¾ããæ¬æ¥ãªãã»ãã·ã§ã³ã¹ãã¬ã¼ã¸ã«ããã¹ãæ å ±ãç½²åä»ãã§ãã©ã¦ã¶ã®ã¯ããã¼ã«ä¿åããæ¯åç½²åæ¤è¨¼ãè¡ãªã£ã¦ããããã§ãã
ã»ãã¥ãªãã£ã«ãé æ ®ãã¦å®è£ ãã¦ãã¾ããoidc-authã§ã¯oauth4webapiã¨ãããã©ã¦ã¶åãã®OAuthã¯ã©ã¤ã¢ã³ãå®è£ ãå©ç¨ãã¦ãããOpenID Connectã®å®è£ ã§å¿ è¦ã¨ãããã»ãã¥ãªãã£å¯¾çã¯å ¨ã¦å®æ½ãã¦ãã¾ã5ãã¾ããã¯ããã¼ã®Secure屿§ãHttpOnly屿§ãã¡ããã¨æå®ãã¦ãã¾ãã
JWTãã»ãã·ã§ã³ã¨ãã¦ä½¿ããã¨ã®æ¯éã«ã¤ãã¦
5å¹´ã»ã©åã«ãSPAãªã©ã®æèã§JWTãã»ãã·ã§ã³ç®¡çã«ä½¿ãã®ãããã®ãæªãã®ããè°è«ã«ãªã£ããã¨ãããã¾ãããã¨ãã°ãã©ããã¦ãªã¹ã¯ã¢ã»ã¹ã¡ã³ãããã« JWT ãã»ãã·ã§ã³ã«ä½¿ã£ã¡ããããï¼ããªã©ãèªãã¨å½æã®è°è«ããããã¾ãã
ç§ã®çè§£ã§ã¯ãã»ãã·ã§ã³ç®¡çã«JWTã使ãã¹ããããªãæ´¾ã®ä¸»å¼µã¯æ¬¡ã®ããã«ã¾ã¨ãããã¾ãã
- ä¸ä¸JWTãæ¼æ´©ããã¨ãã«ã»ãã·ã§ã³ãç¡å¹åããææ®µããªããJWTã®æå¹æéåããå¾ ã¤ãããªã
- ãã°ã¢ã¦ãæã«å½è©²JWTãç¡å¹åã§ããªãï¼ããããå®è£ ãå¤ãï¼
- å é¨ç¯ãJWTç½²åç¨ã®å ±ééµãæªç¨ããã¨ä»»æã®JWTãä½ã£ã¦èªè¨¼ãåé¿ã§ãã
- JWTã¯å·¨å¤§ãã¤æ¤è¨¼ã³ã¹ããé«ãã®ã§ãããã¯ã¼ã¯ã¨CPUã®ç¡é§é£ãã§ãã
- JWTãæ¡ç¨ããã¨ã»ãã·ã§ã³IDæ¹å¼ããè¤é度ãä¸ãã£ã¦ãã°ãä½ãããã
ãããã®ææã®ãã¡ãç¹ã«æåã®ãã®ã¯è´å½çãªåé¡ç¹ã¨è¨ãã¾ããä¸å®è¦æ¨¡ã®çµç¹ã§ã®éç¨ãèããå ´åãå©ç¨è ãPCãç´å¤±ãã¦JWTãæªæãã第ä¸è ã«æ¸¡ãã·ããªãªã¯ç¾å®çãªè å¨ã§ãã䏿¹ãJWTã¯ç½²åæ¤è¨¼ããã¹ããã°JWTã®ä¸èº«ãä¿¡ç¨ããä»çµã¿ãªã®ã§ãJWTãæ¼æ´©ããå ´åã§ã管çè ãç¡å¹åã§ããªãã®ã§ãã
ãã®åé¡ã«å¯¾å¿ãããããä»åå®è£ ããoidc-authã§ã¯JWTå ã«ãªãã¬ãã·ã¥ãã¼ã¯ã³ãæ ¼ç´ããæ¯è¼ççãã¹ãã³ï¼ããã©ã«ã15åï¼ã§æé»çã«ãã¼ã¯ã³åçºè¡ãè¡ã£ã¦ãã¾ããJWTæ¼æ´©ã®æããããå ´åã管çè ãå½è©²ã¦ã¼ã¶ã¼ã®ãªãã¬ãã·ã¥ãã¼ã¯ã³ãç¡å¹åããã°15å以å ã«ã¢ã¯ã»ã¹ã§ããªããªãã¨ããããã§ãã管çè ããªãã¬ãã·ã¥ãã¼ã¯ã³ãç¡å¹åã§ãããã©ããã¯IDãããã¤ãã¼ã«ããã¨æãã¾ãããGoogle WorkspaceãAuth0ãAWS Cognitoãªã©ã§ã¯ç¡å¹åå¯è½ã§ãã
ã¾ãã2ã¤ç®ã®ææç¹ã¸ã®å¯¾å¿ã¨ãã¦ãã°ã¢ã¦ãæã«JWTã«å«ã¾ãããªãã¬ãã·ã¥ãã¼ã¯ã³ãç¡å¹åãã¦ãã¾ã6ãããã«ãããJWTãä»®ã«æ¼æ´©ããã¨ãã¦ã次åã®ãã¼ã¯ã³ãªãã¬ãã·ã¥ã«å¤±æãã¦JWTèªä½ãç¡å¹ã«ãªãä»çµã¿ã«ãªã£ã¦ãã¾ãããã§ã«èª¬æããéãã»ãã·ã§ã³ç¡å¹åã試ã¿ã¦ããä¸å®æéï¼ããã©ã«ã15åï¼ã®ã¿ã¤ã ã©ã°ã許容ããåæãªã®ã§ãã¿ã¤ã ã©ã°1åã§ã許ããªãå ´åã¯ãã®ä»çµã¿èªä½ãæ¡ç¨ã§ããªããã¨ã«ãªãã¾ãã
3ã¤ç®ã®ææç¹ãæ·±å»ãªå 容ã ã¨ç§ã¯èãã¦ãã¾ãã対çã¨ãã¦ã¯å®æçã«éµããã¼ãã¼ãããããããããªãã¨æãã¾ãããç¾ç¶ã ã¨éç¨è ãé å¼µããããªãã®ã§ãoidc-authå´ã§ãä½ãä»çµã¿åãããã¨ããã§ãã
4ã¤ç®ã¯å¤§å¤ããã£ã¨ãã ã¨æãã®ã§ãããã¨ãã¸ã³ã³ãã¥ã¼ãã£ã³ã°ç°å¢ã ã¨ãããã¯ã¼ã¯è»¢ééã¨ã¨ãã¸ã®CPUã¯å®ä¾¡ãªãªã½ã¼ã¹ã§ãããã»ãã·ã§ã³ã¹ãã¬ã¼ã¸ã¯ç¸å¯¾çã«é«ä¾¡ãªã®ã§è¨±ãã¦ã»ãããã¨ããã®ãç§ã®ä¸»å¼µã§ãã
5ã¤ç®ãæ¬å½ã«ãã®éãã§ãã»ãã¥ãªãã£ã®å°éæ§ãä½ãå°äºç®ã®ãã¼ã ãJWTã使ã£ãã»ãã·ã§ã³ç®¡çãèªåå®è£ ããã®ã¯ä¸è¬è«ã¨ãã¦å±éºã ã¨æãã¾ããã¨ã¯ãããä»åç§ã¯ä¸å®ä»¥ä¸æ¤è¨ãã¦ä½ã£ãã¤ããã§ãããOSSã®å½¢ã§ã¿ããªã§å©ãã¦ããã°å®å ¨ãªãã®ãã§ããã®ã§ã¯ãªãã§ããããããããããã¦ãã¦ãèç©ã§ããç°å¢ã¨ãã¦Honoã¯ç´ æ´ããããã©ãããã©ã¼ã ã ã¨æã£ã¦ãã¾ãã
ã¾ã¨ã
OpenID Connectã®ãã°ã¤ã³ã»ãã·ã§ã³ç¶æããã©ã¦ã¶ã¯ããã¼ã ãã§å®ç¾ãããããªHonoã®ããã«ã¦ã§ã¢oidc-authãå®è£ ãã¾ãããCDNã¨ãã¸ã¨ç¸æ§ãè¯ããå®ä¾¡ã«èªè¨¼ã»èªå¯ãå®ç¾ã§ãã¦ä¾¿å©ã ã¨æãã¾ããã¾ãã»ãã¥ãªãã£ã«ã¤ãã¦ãèæ ®ãã¦ããã¤ããã§ãã
ã¨ã¯ããoidc-authã«ã©ã®ç¨åº¦ã®ãã¼ãºããããä½è èªèº«ãããããã£ã¦ãã¾ããã®ã§ã使ã£ã¦ã¿ã¦ã®ææ³ããæè¦ãããã ããã¨å¬ããã§ãã
æå¾ã«ãï¼ããããå¾ä½ã®ãããªãç¶æ ã§ï¼oidc-authãHonoã®3rd-party middlewareã¨ãã¦æ¡ç¨ããã ãã@yusukebeããã«æè¬ãããã¾ãããã®è¨äºãå ã«READMEã«è¿½è¨ãã¦ãããå°ãå®å¿ãã¦ä½¿ããç¶æ ã«ãããã¨æãã¾ãã
- ç°å¢ã«ãã£ã¦ã¯Rustãªã©ã§è¨è¿°ãã¦WebAssemblyã§åä½ããããã¨ãã§ãã¾ãããææåã®é¸æè¢ã¨ã¯è¨ããªãæ°ããã¾ã↩
- ããªã¼ãã©ã³ã ã¨10msã¨ããã«å¶ç´ãå³ãããªãã¾ããã¾ãã50msããé·æé使ããå¥ãã©ã³ãæä¾ããã¦ãã¾ãã↩
- ããã¾ã§ç§ã®è§£éãªã®ã§ãéã£ãå©ç¹ã«çç®ãã¦ããã¦ã¼ã¶ã¼ãå¤ãã¨æãã¾ã↩
- IDãã¼ã¯ã³ã®subãã£ã¼ã«ã↩
- oauth4webapièªä½ãå©ç¨è ãå¤ãç¶ç¶çã«ã¡ã³ããã³ã¹ããã¦ããã©ã¤ãã©ãªã§ãOpenID Connectã§å¿ é ã¨ãããstateãã©ã¡ã¼ã¿ãnonceãã©ã¡ã¼ã¿ãcode_challengeãã©ã¡ã¼ã¿ã®3ã¤ã®æ¤è¨¼ãæ¨æºã§ãµãã¼ããã¦ãã¾ããã¾ããalg:noneãæå¦ãããªã©ã»ãã¥ãªãã£è¦³ç¹ã®ãã¹ããã©ã¯ãã£ã¹ãå®è£ ããã¦ããããã«æãã¾ãã↩
- æ¬ç¨¿ãµã³ãã«ã³ã¼ããã·ã¼ã±ã³ã¹å³ã§ã¯ãã°ã¢ã¦ãå¦çãçç¥ãã¦ãã¾ãããã¡ããã¨å®è£ ãã¦ããã¾ã↩