[{"data":1,"prerenderedAt":2006},["ShallowReactive",2],{"post-nestjs-hasura-auth0-authentication":3},{"id":4,"title":5,"body":6,"date":1993,"description":1994,"extension":1995,"meta":1996,"navigation":196,"path":2002,"seo":2003,"stem":2004,"__hash__":2005},"blog\u002Fblog\u002Fnestjs-hasura-auth0-authentication.md","NestJSとHasuraをAuth0で認証する実装ガイド",{"type":7,"value":8,"toc":1974},"minimark",[9,13,17,26,29,52,56,60,63,79,83,86,90,98,101,325,329,332,335,383,390,394,403,407,427,431,1159,1162,1358,1362,1617,1620,1623,1626,1634,1822,1826,1833,1941,1944,1947,1967,1970],[10,11,12],"h2",{"id":12},"はじめに",[14,15,16],"p",{},"NestJSのAPIエンドポイントとHasuraのGraphQLに対する認証基盤としてAuth0を採用した際の実装方法を解説します。",[14,18,19],{},[20,21,25],"a",{"href":22,"rel":23},"https:\u002F\u002Fauth0.com\u002Fjp\u002Fauthentication",[24],"nofollow","Auth0公式サイト",[10,27,28],{"id":28},"アーキテクチャ",[30,31,32,40,46],"ul",{},[33,34,35,39],"li",{},[36,37,38],"strong",{},"Hasura"," GraphQLエンドポイントを提供。シンプルなCRUD操作を担当",[33,41,42,45],{},[36,43,44],{},"NestJS"," Hasuraで吸収できない複雑なビジネスロジックを担当",[33,47,48,51],{},[36,49,50],{},"Auth0"," 両方のエンドポイントを統一的に認証",[10,53,55],{"id":54},"hasuraの認証設定","Hasuraの認証設定",[57,58,59],"h3",{"id":59},"基本設定",[14,61,62],{},"Hasuraの公式チュートリアルに従って設定を進めます。",[30,64,65,72],{},[33,66,67],{},[20,68,71],{"href":69,"rel":70},"https:\u002F\u002Fhasura.io\u002Flearn\u002Fja\u002Fgraphql\u002Fhasura\u002Fintroduction\u002F",[24],"Hasura認証チュートリアル",[33,73,74],{},[20,75,78],{"href":76,"rel":77},"https:\u002F\u002Fhub.docker.com\u002Fr\u002Fhasura\u002Fgraphql-engine",[24],"Hasura Docker Hub",[57,80,82],{"id":81},"auth0公開鍵の設定","Auth0公開鍵の設定",[14,84,85],{},"Docker環境の場合、Auth0から公開鍵を取得し、環境変数に設定してください。",[87,88,89],"h4",{"id":89},"公開鍵の取得",[14,91,92,97],{},[20,93,96],{"href":94,"rel":95},"https:\u002F\u002Fhasura.io\u002Flearn\u002Fja\u002Fgraphql\u002Fhasura\u002Fauthentication\u002F3-setup-env-vars-hasura\u002F",[24],"公開鍵取得手順","に従って公開鍵を発行します。",[87,99,100],{"id":100},"docker-compose設定",[102,103,108],"pre",{"className":104,"code":105,"language":106,"meta":107,"style":107},"language-yaml shiki shiki-themes github-light github-dark","# docker-compose.yml\nversion: '3.6'\nservices:\n  postgres:\n    image: postgres\n    restart: always\n    volumes:\n      - db_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n\n  graphql-engine:\n    image: hasura\u002Fgraphql-engine:v1.0.0-beta.6\n    ports:\n      - \"8080:8080\"\n    depends_on:\n      - \"postgres\"\n    restart: always\n    environment:\n      HASURA_GRAPHQL_DATABASE_URL: postgres:\u002F\u002Fpostgres:@postgres:5432\u002Fpostgres\n      HASURA_GRAPHQL_ENABLE_CONSOLE: \"true\"\n      # HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey\n      HASURA_GRAPHQL_JWT_SECRET: '取得した公開鍵を設定'\n\nvolumes:\n  db_data:\n","yaml","",[109,110,111,120,135,144,152,163,174,182,191,198,206,216,224,232,240,248,257,265,276,287,293,304,309,317],"code",{"__ignoreMap":107},[112,113,116],"span",{"class":114,"line":115},"line",1,[112,117,119],{"class":118},"sJ8bj","# docker-compose.yml\n",[112,121,123,127,131],{"class":114,"line":122},2,[112,124,126],{"class":125},"s9eBZ","version",[112,128,130],{"class":129},"sVt8B",": ",[112,132,134],{"class":133},"sZZnC","'3.6'\n",[112,136,138,141],{"class":114,"line":137},3,[112,139,140],{"class":125},"services",[112,142,143],{"class":129},":\n",[112,145,147,150],{"class":114,"line":146},4,[112,148,149],{"class":125},"  postgres",[112,151,143],{"class":129},[112,153,155,158,160],{"class":114,"line":154},5,[112,156,157],{"class":125},"    image",[112,159,130],{"class":129},[112,161,162],{"class":133},"postgres\n",[112,164,166,169,171],{"class":114,"line":165},6,[112,167,168],{"class":125},"    restart",[112,170,130],{"class":129},[112,172,173],{"class":133},"always\n",[112,175,177,180],{"class":114,"line":176},7,[112,178,179],{"class":125},"    volumes",[112,181,143],{"class":129},[112,183,185,188],{"class":114,"line":184},8,[112,186,187],{"class":129},"      - ",[112,189,190],{"class":133},"db_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n",[112,192,194],{"class":114,"line":193},9,[112,195,197],{"emptyLinePlaceholder":196},true,"\n",[112,199,201,204],{"class":114,"line":200},10,[112,202,203],{"class":125},"  graphql-engine",[112,205,143],{"class":129},[112,207,209,211,213],{"class":114,"line":208},11,[112,210,157],{"class":125},[112,212,130],{"class":129},[112,214,215],{"class":133},"hasura\u002Fgraphql-engine:v1.0.0-beta.6\n",[112,217,219,222],{"class":114,"line":218},12,[112,220,221],{"class":125},"    ports",[112,223,143],{"class":129},[112,225,227,229],{"class":114,"line":226},13,[112,228,187],{"class":129},[112,230,231],{"class":133},"\"8080:8080\"\n",[112,233,235,238],{"class":114,"line":234},14,[112,236,237],{"class":125},"    depends_on",[112,239,143],{"class":129},[112,241,243,245],{"class":114,"line":242},15,[112,244,187],{"class":129},[112,246,247],{"class":133},"\"postgres\"\n",[112,249,251,253,255],{"class":114,"line":250},16,[112,252,168],{"class":125},[112,254,130],{"class":129},[112,256,173],{"class":133},[112,258,260,263],{"class":114,"line":259},17,[112,261,262],{"class":125},"    environment",[112,264,143],{"class":129},[112,266,268,271,273],{"class":114,"line":267},18,[112,269,270],{"class":125},"      HASURA_GRAPHQL_DATABASE_URL",[112,272,130],{"class":129},[112,274,275],{"class":133},"postgres:\u002F\u002Fpostgres:@postgres:5432\u002Fpostgres\n",[112,277,279,282,284],{"class":114,"line":278},19,[112,280,281],{"class":125},"      HASURA_GRAPHQL_ENABLE_CONSOLE",[112,283,130],{"class":129},[112,285,286],{"class":133},"\"true\"\n",[112,288,290],{"class":114,"line":289},20,[112,291,292],{"class":118},"      # HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey\n",[112,294,296,299,301],{"class":114,"line":295},21,[112,297,298],{"class":125},"      HASURA_GRAPHQL_JWT_SECRET",[112,300,130],{"class":129},[112,302,303],{"class":133},"'取得した公開鍵を設定'\n",[112,305,307],{"class":114,"line":306},22,[112,308,197],{"emptyLinePlaceholder":196},[112,310,312,315],{"class":114,"line":311},23,[112,313,314],{"class":125},"volumes",[112,316,143],{"class":129},[112,318,320,323],{"class":114,"line":319},24,[112,321,322],{"class":125},"  db_data",[112,324,143],{"class":129},[10,326,328],{"id":327},"nestjsの認証実装","NestJSの認証実装",[14,330,331],{},"NestJS側のAPIエンドポイントを直接呼び出されないよう、Auth0で保護します。",[57,333,334],{"id":334},"環境変数の設定",[102,336,340],{"className":337,"code":338,"language":339,"meta":107,"style":107},"language-bash shiki shiki-themes github-light github-dark","# .env\n# Auth0 Domain\nAUTH0_ISSUER_URL=\"https:\u002F\u002Fyour-domain.auth0.com\u002F\"\n\n# Auth0 Identifier\nAUTH0_AUDIENCE=\"your-api-identifier\"\n","bash",[109,341,342,347,352,364,368,373],{"__ignoreMap":107},[112,343,344],{"class":114,"line":115},[112,345,346],{"class":118},"# .env\n",[112,348,349],{"class":114,"line":122},[112,350,351],{"class":118},"# Auth0 Domain\n",[112,353,354,357,361],{"class":114,"line":137},[112,355,356],{"class":129},"AUTH0_ISSUER_URL",[112,358,360],{"class":359},"szBVR","=",[112,362,363],{"class":133},"\"https:\u002F\u002Fyour-domain.auth0.com\u002F\"\n",[112,365,366],{"class":114,"line":146},[112,367,197],{"emptyLinePlaceholder":196},[112,369,370],{"class":114,"line":154},[112,371,372],{"class":118},"# Auth0 Identifier\n",[112,374,375,378,380],{"class":114,"line":165},[112,376,377],{"class":129},"AUTH0_AUDIENCE",[112,379,360],{"class":359},[112,381,382],{"class":133},"\"your-api-identifier\"\n",[14,384,385,386,389],{},"本番環境では、",[109,387,388],{},".env","ファイルではなく、Cloud Run Secretsなどのシークレット管理サービスを使用することを推奨します。",[57,391,393],{"id":392},"authguardの実装","AuthGuardの実装",[14,395,396,397,402],{},"NestJSの",[20,398,401],{"href":399,"rel":400},"https:\u002F\u002Fdocs.nestjs.com\u002Fguards",[24],"Guard","機能を使って、リクエストの認証を検証します。",[87,404,406],{"id":405},"guardの作成","Guardの作成",[102,408,410],{"className":337,"code":409,"language":339,"meta":107,"style":107},"nest g guard auth\u002Fauth-guard\n",[109,411,412],{"__ignoreMap":107},[112,413,414,418,421,424],{"class":114,"line":115},[112,415,417],{"class":416},"sScJk","nest",[112,419,420],{"class":133}," g",[112,422,423],{"class":133}," guard",[112,425,426],{"class":133}," auth\u002Fauth-guard\n",[87,428,430],{"id":429},"guardの実装","Guardの実装",[102,432,436],{"className":433,"code":434,"language":435,"meta":107,"style":107},"language-typescript shiki shiki-themes github-light github-dark","\u002F\u002F src\u002Fcommon\u002Fguard\u002Fauth\u002Fauth-guard.guard.ts\nimport {\n  CanActivate,\n  ExecutionContext,\n  Injectable,\n  UnauthorizedException,\n} from '@nestjs\u002Fcommon'\nimport { GqlContextType } from '@nestjs\u002Fgraphql'\nimport { InjectPinoLogger, PinoLogger } from 'nestjs-pino'\nimport { Reflector } from '@nestjs\u002Fcore'\nimport { expressjwt, GetVerificationKey } from 'express-jwt'\nimport { expressJwtSecret } from 'jwks-rsa'\nimport { ConfigService } from '@nestjs\u002Fconfig'\nimport { promisify } from 'util'\n\n@Injectable()\nexport class AuthGuard implements CanActivate {\n  private readonly AUTH0_AUDIENCE: string\n  private readonly AUTH0_ISSUER_URL: string\n\n  constructor(\n    @InjectPinoLogger(AuthGuard.name) private readonly logger: PinoLogger,\n    private readonly reflector: Reflector,\n    private readonly configService: ConfigService,\n  ) {\n    this.AUTH0_AUDIENCE = this.configService.get('AUTH0_AUDIENCE')\n    this.AUTH0_ISSUER_URL = this.configService.get('AUTH0_ISSUER_URL')\n  }\n\n  async canActivate(context: ExecutionContext): Promise\u003Cboolean> {\n    \u002F\u002F GraphQLリクエストはスキップ（Hasura経由の場合）\n    if (context.getType\u003CGqlContextType>() === 'graphql') {\n      return true\n    }\n\n    \u002F\u002F Auth0に対してJWT Tokenの検証を実行\n    const checkJwtToken = await promisify(\n      expressjwt({\n        secret: expressJwtSecret({\n          cache: true,\n          rateLimit: true,\n          jwksRequestsPerMinute: 5,\n          jwksUri: `${process.env.AUTH0_ISSUER_URL}.well-known\u002Fjwks.json`,\n        }) as GetVerificationKey,\n        audience: this.AUTH0_AUDIENCE,\n        issuer: this.AUTH0_ISSUER_URL,\n        algorithms: ['RS256'],\n      }),\n    )\n\n    try {\n      await checkJwtToken(\n        context.switchToHttp().getRequest(),\n        context.switchToHttp().getResponse(),\n      )\n      return true\n    } catch (e: unknown) {\n      throw new UnauthorizedException(e)\n    }\n  }\n}\n","typescript",[109,437,438,443,451,456,461,466,471,482,494,506,518,530,542,554,566,570,581,600,619,632,636,644,671,688,704,710,742,766,772,777,813,819,848,857,863,868,874,893,902,913,924,934,945,971,985,1000,1014,1026,1032,1038,1043,1051,1061,1079,1093,1099,1106,1128,1143,1148,1153],{"__ignoreMap":107},[112,439,440],{"class":114,"line":115},[112,441,442],{"class":118},"\u002F\u002F src\u002Fcommon\u002Fguard\u002Fauth\u002Fauth-guard.guard.ts\n",[112,444,445,448],{"class":114,"line":122},[112,446,447],{"class":359},"import",[112,449,450],{"class":129}," {\n",[112,452,453],{"class":114,"line":137},[112,454,455],{"class":129},"  CanActivate,\n",[112,457,458],{"class":114,"line":146},[112,459,460],{"class":129},"  ExecutionContext,\n",[112,462,463],{"class":114,"line":154},[112,464,465],{"class":129},"  Injectable,\n",[112,467,468],{"class":114,"line":165},[112,469,470],{"class":129},"  UnauthorizedException,\n",[112,472,473,476,479],{"class":114,"line":176},[112,474,475],{"class":129},"} ",[112,477,478],{"class":359},"from",[112,480,481],{"class":133}," '@nestjs\u002Fcommon'\n",[112,483,484,486,489,491],{"class":114,"line":184},[112,485,447],{"class":359},[112,487,488],{"class":129}," { GqlContextType } ",[112,490,478],{"class":359},[112,492,493],{"class":133}," '@nestjs\u002Fgraphql'\n",[112,495,496,498,501,503],{"class":114,"line":193},[112,497,447],{"class":359},[112,499,500],{"class":129}," { InjectPinoLogger, PinoLogger } ",[112,502,478],{"class":359},[112,504,505],{"class":133}," 'nestjs-pino'\n",[112,507,508,510,513,515],{"class":114,"line":200},[112,509,447],{"class":359},[112,511,512],{"class":129}," { Reflector } ",[112,514,478],{"class":359},[112,516,517],{"class":133}," '@nestjs\u002Fcore'\n",[112,519,520,522,525,527],{"class":114,"line":208},[112,521,447],{"class":359},[112,523,524],{"class":129}," { expressjwt, GetVerificationKey } ",[112,526,478],{"class":359},[112,528,529],{"class":133}," 'express-jwt'\n",[112,531,532,534,537,539],{"class":114,"line":218},[112,533,447],{"class":359},[112,535,536],{"class":129}," { expressJwtSecret } ",[112,538,478],{"class":359},[112,540,541],{"class":133}," 'jwks-rsa'\n",[112,543,544,546,549,551],{"class":114,"line":226},[112,545,447],{"class":359},[112,547,548],{"class":129}," { ConfigService } ",[112,550,478],{"class":359},[112,552,553],{"class":133}," '@nestjs\u002Fconfig'\n",[112,555,556,558,561,563],{"class":114,"line":234},[112,557,447],{"class":359},[112,559,560],{"class":129}," { promisify } ",[112,562,478],{"class":359},[112,564,565],{"class":133}," 'util'\n",[112,567,568],{"class":114,"line":242},[112,569,197],{"emptyLinePlaceholder":196},[112,571,572,575,578],{"class":114,"line":250},[112,573,574],{"class":129},"@",[112,576,577],{"class":416},"Injectable",[112,579,580],{"class":129},"()\n",[112,582,583,586,589,592,595,598],{"class":114,"line":259},[112,584,585],{"class":359},"export",[112,587,588],{"class":359}," class",[112,590,591],{"class":416}," AuthGuard",[112,593,594],{"class":359}," implements",[112,596,597],{"class":416}," CanActivate",[112,599,450],{"class":129},[112,601,602,605,608,612,615],{"class":114,"line":267},[112,603,604],{"class":359},"  private",[112,606,607],{"class":359}," readonly",[112,609,611],{"class":610},"s4XuR"," AUTH0_AUDIENCE",[112,613,614],{"class":359},":",[112,616,618],{"class":617},"sj4cs"," string\n",[112,620,621,623,625,628,630],{"class":114,"line":278},[112,622,604],{"class":359},[112,624,607],{"class":359},[112,626,627],{"class":610}," AUTH0_ISSUER_URL",[112,629,614],{"class":359},[112,631,618],{"class":617},[112,633,634],{"class":114,"line":289},[112,635,197],{"emptyLinePlaceholder":196},[112,637,638,641],{"class":114,"line":295},[112,639,640],{"class":359},"  constructor",[112,642,643],{"class":129},"(\n",[112,645,646,649,652,655,658,660,663,665,668],{"class":114,"line":306},[112,647,648],{"class":129},"    @",[112,650,651],{"class":416},"InjectPinoLogger",[112,653,654],{"class":129},"(AuthGuard.name) ",[112,656,657],{"class":359},"private",[112,659,607],{"class":359},[112,661,662],{"class":610}," logger",[112,664,614],{"class":359},[112,666,667],{"class":416}," PinoLogger",[112,669,670],{"class":129},",\n",[112,672,673,676,678,681,683,686],{"class":114,"line":311},[112,674,675],{"class":359},"    private",[112,677,607],{"class":359},[112,679,680],{"class":610}," reflector",[112,682,614],{"class":359},[112,684,685],{"class":416}," Reflector",[112,687,670],{"class":129},[112,689,690,692,694,697,699,702],{"class":114,"line":319},[112,691,675],{"class":359},[112,693,607],{"class":359},[112,695,696],{"class":610}," configService",[112,698,614],{"class":359},[112,700,701],{"class":416}," ConfigService",[112,703,670],{"class":129},[112,705,707],{"class":114,"line":706},25,[112,708,709],{"class":129},"  ) {\n",[112,711,713,716,719,721,724,727,730,733,736,739],{"class":114,"line":712},26,[112,714,715],{"class":617},"    this",[112,717,718],{"class":129},".",[112,720,377],{"class":617},[112,722,723],{"class":359}," =",[112,725,726],{"class":617}," this",[112,728,729],{"class":129},".configService.",[112,731,732],{"class":416},"get",[112,734,735],{"class":129},"(",[112,737,738],{"class":133},"'AUTH0_AUDIENCE'",[112,740,741],{"class":129},")\n",[112,743,745,747,749,751,753,755,757,759,761,764],{"class":114,"line":744},27,[112,746,715],{"class":617},[112,748,718],{"class":129},[112,750,356],{"class":617},[112,752,723],{"class":359},[112,754,726],{"class":617},[112,756,729],{"class":129},[112,758,732],{"class":416},[112,760,735],{"class":129},[112,762,763],{"class":133},"'AUTH0_ISSUER_URL'",[112,765,741],{"class":129},[112,767,769],{"class":114,"line":768},28,[112,770,771],{"class":129},"  }\n",[112,773,775],{"class":114,"line":774},29,[112,776,197],{"emptyLinePlaceholder":196},[112,778,780,783,786,788,791,793,796,799,801,804,807,810],{"class":114,"line":779},30,[112,781,782],{"class":359},"  async",[112,784,785],{"class":416}," canActivate",[112,787,735],{"class":129},[112,789,790],{"class":610},"context",[112,792,614],{"class":359},[112,794,795],{"class":416}," ExecutionContext",[112,797,798],{"class":129},")",[112,800,614],{"class":359},[112,802,803],{"class":416}," Promise",[112,805,806],{"class":129},"\u003C",[112,808,809],{"class":617},"boolean",[112,811,812],{"class":129},"> {\n",[112,814,816],{"class":114,"line":815},31,[112,817,818],{"class":118},"    \u002F\u002F GraphQLリクエストはスキップ（Hasura経由の場合）\n",[112,820,822,825,828,831,833,836,839,842,845],{"class":114,"line":821},32,[112,823,824],{"class":359},"    if",[112,826,827],{"class":129}," (context.",[112,829,830],{"class":416},"getType",[112,832,806],{"class":129},[112,834,835],{"class":416},"GqlContextType",[112,837,838],{"class":129},">() ",[112,840,841],{"class":359},"===",[112,843,844],{"class":133}," 'graphql'",[112,846,847],{"class":129},") {\n",[112,849,851,854],{"class":114,"line":850},33,[112,852,853],{"class":359},"      return",[112,855,856],{"class":617}," true\n",[112,858,860],{"class":114,"line":859},34,[112,861,862],{"class":129},"    }\n",[112,864,866],{"class":114,"line":865},35,[112,867,197],{"emptyLinePlaceholder":196},[112,869,871],{"class":114,"line":870},36,[112,872,873],{"class":118},"    \u002F\u002F Auth0に対してJWT Tokenの検証を実行\n",[112,875,877,880,883,885,888,891],{"class":114,"line":876},37,[112,878,879],{"class":359},"    const",[112,881,882],{"class":617}," checkJwtToken",[112,884,723],{"class":359},[112,886,887],{"class":359}," await",[112,889,890],{"class":416}," promisify",[112,892,643],{"class":129},[112,894,896,899],{"class":114,"line":895},38,[112,897,898],{"class":416},"      expressjwt",[112,900,901],{"class":129},"({\n",[112,903,905,908,911],{"class":114,"line":904},39,[112,906,907],{"class":129},"        secret: ",[112,909,910],{"class":416},"expressJwtSecret",[112,912,901],{"class":129},[112,914,916,919,922],{"class":114,"line":915},40,[112,917,918],{"class":129},"          cache: ",[112,920,921],{"class":617},"true",[112,923,670],{"class":129},[112,925,927,930,932],{"class":114,"line":926},41,[112,928,929],{"class":129},"          rateLimit: ",[112,931,921],{"class":617},[112,933,670],{"class":129},[112,935,937,940,943],{"class":114,"line":936},42,[112,938,939],{"class":129},"          jwksRequestsPerMinute: ",[112,941,942],{"class":617},"5",[112,944,670],{"class":129},[112,946,948,951,954,957,959,962,964,966,969],{"class":114,"line":947},43,[112,949,950],{"class":129},"          jwksUri: ",[112,952,953],{"class":133},"`${",[112,955,956],{"class":129},"process",[112,958,718],{"class":133},[112,960,961],{"class":129},"env",[112,963,718],{"class":133},[112,965,356],{"class":617},[112,967,968],{"class":133},"}.well-known\u002Fjwks.json`",[112,970,670],{"class":129},[112,972,974,977,980,983],{"class":114,"line":973},44,[112,975,976],{"class":129},"        }) ",[112,978,979],{"class":359},"as",[112,981,982],{"class":416}," GetVerificationKey",[112,984,670],{"class":129},[112,986,988,991,994,996,998],{"class":114,"line":987},45,[112,989,990],{"class":129},"        audience: ",[112,992,993],{"class":617},"this",[112,995,718],{"class":129},[112,997,377],{"class":617},[112,999,670],{"class":129},[112,1001,1003,1006,1008,1010,1012],{"class":114,"line":1002},46,[112,1004,1005],{"class":129},"        issuer: ",[112,1007,993],{"class":617},[112,1009,718],{"class":129},[112,1011,356],{"class":617},[112,1013,670],{"class":129},[112,1015,1017,1020,1023],{"class":114,"line":1016},47,[112,1018,1019],{"class":129},"        algorithms: [",[112,1021,1022],{"class":133},"'RS256'",[112,1024,1025],{"class":129},"],\n",[112,1027,1029],{"class":114,"line":1028},48,[112,1030,1031],{"class":129},"      }),\n",[112,1033,1035],{"class":114,"line":1034},49,[112,1036,1037],{"class":129},"    )\n",[112,1039,1041],{"class":114,"line":1040},50,[112,1042,197],{"emptyLinePlaceholder":196},[112,1044,1046,1049],{"class":114,"line":1045},51,[112,1047,1048],{"class":359},"    try",[112,1050,450],{"class":129},[112,1052,1054,1057,1059],{"class":114,"line":1053},52,[112,1055,1056],{"class":359},"      await",[112,1058,882],{"class":416},[112,1060,643],{"class":129},[112,1062,1064,1067,1070,1073,1076],{"class":114,"line":1063},53,[112,1065,1066],{"class":129},"        context.",[112,1068,1069],{"class":416},"switchToHttp",[112,1071,1072],{"class":129},"().",[112,1074,1075],{"class":416},"getRequest",[112,1077,1078],{"class":129},"(),\n",[112,1080,1082,1084,1086,1088,1091],{"class":114,"line":1081},54,[112,1083,1066],{"class":129},[112,1085,1069],{"class":416},[112,1087,1072],{"class":129},[112,1089,1090],{"class":416},"getResponse",[112,1092,1078],{"class":129},[112,1094,1096],{"class":114,"line":1095},55,[112,1097,1098],{"class":129},"      )\n",[112,1100,1102,1104],{"class":114,"line":1101},56,[112,1103,853],{"class":359},[112,1105,856],{"class":617},[112,1107,1109,1112,1115,1118,1121,1123,1126],{"class":114,"line":1108},57,[112,1110,1111],{"class":129},"    } ",[112,1113,1114],{"class":359},"catch",[112,1116,1117],{"class":129}," (",[112,1119,1120],{"class":610},"e",[112,1122,614],{"class":359},[112,1124,1125],{"class":617}," unknown",[112,1127,847],{"class":129},[112,1129,1131,1134,1137,1140],{"class":114,"line":1130},58,[112,1132,1133],{"class":359},"      throw",[112,1135,1136],{"class":359}," new",[112,1138,1139],{"class":416}," UnauthorizedException",[112,1141,1142],{"class":129},"(e)\n",[112,1144,1146],{"class":114,"line":1145},59,[112,1147,862],{"class":129},[112,1149,1151],{"class":114,"line":1150},60,[112,1152,771],{"class":129},[112,1154,1156],{"class":114,"line":1155},61,[112,1157,1158],{"class":129},"}\n",[57,1160,1161],{"id":1161},"エンドポイントへの適用",[102,1163,1165],{"className":433,"code":1164,"language":435,"meta":107,"style":107},"\u002F\u002F src\u002Fapp.controller.ts\nimport { Controller, Get, UseGuards } from '@nestjs\u002Fcommon'\nimport { AppService } from '.\u002Fapp.service'\nimport { AuthGuard } from '.\u002Fcommon\u002Fguard\u002Fauth\u002Fauth-guard.guard'\n\n@Controller()\nexport class AppController {\n  constructor(private readonly appService: AppService) {}\n\n  @Get()\n  getHello(): string {\n    return this.appService.getHello()\n  }\n\n  @UseGuards(AuthGuard)\n  @Get('\u002Fprivate')\n  async private() {\n    return { message: '認証成功' }\n  }\n}\n",[109,1166,1167,1172,1183,1195,1207,1211,1220,1231,1252,1256,1266,1281,1296,1300,1304,1314,1327,1337,1350,1354],{"__ignoreMap":107},[112,1168,1169],{"class":114,"line":115},[112,1170,1171],{"class":118},"\u002F\u002F src\u002Fapp.controller.ts\n",[112,1173,1174,1176,1179,1181],{"class":114,"line":122},[112,1175,447],{"class":359},[112,1177,1178],{"class":129}," { Controller, Get, UseGuards } ",[112,1180,478],{"class":359},[112,1182,481],{"class":133},[112,1184,1185,1187,1190,1192],{"class":114,"line":137},[112,1186,447],{"class":359},[112,1188,1189],{"class":129}," { AppService } ",[112,1191,478],{"class":359},[112,1193,1194],{"class":133}," '.\u002Fapp.service'\n",[112,1196,1197,1199,1202,1204],{"class":114,"line":146},[112,1198,447],{"class":359},[112,1200,1201],{"class":129}," { AuthGuard } ",[112,1203,478],{"class":359},[112,1205,1206],{"class":133}," '.\u002Fcommon\u002Fguard\u002Fauth\u002Fauth-guard.guard'\n",[112,1208,1209],{"class":114,"line":154},[112,1210,197],{"emptyLinePlaceholder":196},[112,1212,1213,1215,1218],{"class":114,"line":165},[112,1214,574],{"class":129},[112,1216,1217],{"class":416},"Controller",[112,1219,580],{"class":129},[112,1221,1222,1224,1226,1229],{"class":114,"line":176},[112,1223,585],{"class":359},[112,1225,588],{"class":359},[112,1227,1228],{"class":416}," AppController",[112,1230,450],{"class":129},[112,1232,1233,1235,1237,1239,1241,1244,1246,1249],{"class":114,"line":184},[112,1234,640],{"class":359},[112,1236,735],{"class":129},[112,1238,657],{"class":359},[112,1240,607],{"class":359},[112,1242,1243],{"class":610}," appService",[112,1245,614],{"class":359},[112,1247,1248],{"class":416}," AppService",[112,1250,1251],{"class":129},") {}\n",[112,1253,1254],{"class":114,"line":193},[112,1255,197],{"emptyLinePlaceholder":196},[112,1257,1258,1261,1264],{"class":114,"line":200},[112,1259,1260],{"class":129},"  @",[112,1262,1263],{"class":416},"Get",[112,1265,580],{"class":129},[112,1267,1268,1271,1274,1276,1279],{"class":114,"line":208},[112,1269,1270],{"class":416},"  getHello",[112,1272,1273],{"class":129},"()",[112,1275,614],{"class":359},[112,1277,1278],{"class":617}," string",[112,1280,450],{"class":129},[112,1282,1283,1286,1288,1291,1294],{"class":114,"line":218},[112,1284,1285],{"class":359},"    return",[112,1287,726],{"class":617},[112,1289,1290],{"class":129},".appService.",[112,1292,1293],{"class":416},"getHello",[112,1295,580],{"class":129},[112,1297,1298],{"class":114,"line":226},[112,1299,771],{"class":129},[112,1301,1302],{"class":114,"line":234},[112,1303,197],{"emptyLinePlaceholder":196},[112,1305,1306,1308,1311],{"class":114,"line":242},[112,1307,1260],{"class":129},[112,1309,1310],{"class":416},"UseGuards",[112,1312,1313],{"class":129},"(AuthGuard)\n",[112,1315,1316,1318,1320,1322,1325],{"class":114,"line":250},[112,1317,1260],{"class":129},[112,1319,1263],{"class":416},[112,1321,735],{"class":129},[112,1323,1324],{"class":133},"'\u002Fprivate'",[112,1326,741],{"class":129},[112,1328,1329,1331,1334],{"class":114,"line":259},[112,1330,782],{"class":359},[112,1332,1333],{"class":416}," private",[112,1335,1336],{"class":129},"() {\n",[112,1338,1339,1341,1344,1347],{"class":114,"line":267},[112,1340,1285],{"class":359},[112,1342,1343],{"class":129}," { message: ",[112,1345,1346],{"class":133},"'認証成功'",[112,1348,1349],{"class":129}," }\n",[112,1351,1352],{"class":114,"line":278},[112,1353,771],{"class":129},[112,1355,1356],{"class":114,"line":289},[112,1357,1158],{"class":129},[57,1359,1361],{"id":1360},"cors設定","CORS設定",[102,1363,1365],{"className":433,"code":1364,"language":435,"meta":107,"style":107},"\u002F\u002F src\u002Fmain.ts\nimport { HttpAdapterHost, NestFactory } from '@nestjs\u002Fcore'\nimport { AppModule } from '.\u002Fapp.module'\nimport { Logger } from 'nestjs-pino'\nimport { AllExceptionsFilter } from '.\u002Fcommon\u002Ffilter\u002Fall-exceptions.filter'\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule, { bufferLogs: true })\n  app.useLogger(app.get(Logger))\n\n  const adapterHost = app.get(HttpAdapterHost)\n  const httpAdapter = adapterHost.httpAdapter\n  const instance = httpAdapter.getInstance()\n  app.useGlobalFilters(new AllExceptionsFilter(instance))\n\n  app.enableCors({\n    origin: '*',\n    allowedHeaders:\n      'Origin, X-Requested-With, Content-Type, Accept, Authorization',\n  })\n\n  await app.listen(3000)\n}\nbootstrap()\n",[109,1366,1367,1372,1383,1395,1406,1418,1422,1435,1461,1477,1481,1498,1510,1527,1545,1549,1558,1568,1573,1580,1585,1589,1606,1610],{"__ignoreMap":107},[112,1368,1369],{"class":114,"line":115},[112,1370,1371],{"class":118},"\u002F\u002F src\u002Fmain.ts\n",[112,1373,1374,1376,1379,1381],{"class":114,"line":122},[112,1375,447],{"class":359},[112,1377,1378],{"class":129}," { HttpAdapterHost, NestFactory } ",[112,1380,478],{"class":359},[112,1382,517],{"class":133},[112,1384,1385,1387,1390,1392],{"class":114,"line":137},[112,1386,447],{"class":359},[112,1388,1389],{"class":129}," { AppModule } ",[112,1391,478],{"class":359},[112,1393,1394],{"class":133}," '.\u002Fapp.module'\n",[112,1396,1397,1399,1402,1404],{"class":114,"line":146},[112,1398,447],{"class":359},[112,1400,1401],{"class":129}," { Logger } ",[112,1403,478],{"class":359},[112,1405,505],{"class":133},[112,1407,1408,1410,1413,1415],{"class":114,"line":154},[112,1409,447],{"class":359},[112,1411,1412],{"class":129}," { AllExceptionsFilter } ",[112,1414,478],{"class":359},[112,1416,1417],{"class":133}," '.\u002Fcommon\u002Ffilter\u002Fall-exceptions.filter'\n",[112,1419,1420],{"class":114,"line":165},[112,1421,197],{"emptyLinePlaceholder":196},[112,1423,1424,1427,1430,1433],{"class":114,"line":176},[112,1425,1426],{"class":359},"async",[112,1428,1429],{"class":359}," function",[112,1431,1432],{"class":416}," bootstrap",[112,1434,1336],{"class":129},[112,1436,1437,1440,1443,1445,1447,1450,1453,1456,1458],{"class":114,"line":184},[112,1438,1439],{"class":359},"  const",[112,1441,1442],{"class":617}," app",[112,1444,723],{"class":359},[112,1446,887],{"class":359},[112,1448,1449],{"class":129}," NestFactory.",[112,1451,1452],{"class":416},"create",[112,1454,1455],{"class":129},"(AppModule, { bufferLogs: ",[112,1457,921],{"class":617},[112,1459,1460],{"class":129}," })\n",[112,1462,1463,1466,1469,1472,1474],{"class":114,"line":193},[112,1464,1465],{"class":129},"  app.",[112,1467,1468],{"class":416},"useLogger",[112,1470,1471],{"class":129},"(app.",[112,1473,732],{"class":416},[112,1475,1476],{"class":129},"(Logger))\n",[112,1478,1479],{"class":114,"line":200},[112,1480,197],{"emptyLinePlaceholder":196},[112,1482,1483,1485,1488,1490,1493,1495],{"class":114,"line":208},[112,1484,1439],{"class":359},[112,1486,1487],{"class":617}," adapterHost",[112,1489,723],{"class":359},[112,1491,1492],{"class":129}," app.",[112,1494,732],{"class":416},[112,1496,1497],{"class":129},"(HttpAdapterHost)\n",[112,1499,1500,1502,1505,1507],{"class":114,"line":218},[112,1501,1439],{"class":359},[112,1503,1504],{"class":617}," httpAdapter",[112,1506,723],{"class":359},[112,1508,1509],{"class":129}," adapterHost.httpAdapter\n",[112,1511,1512,1514,1517,1519,1522,1525],{"class":114,"line":226},[112,1513,1439],{"class":359},[112,1515,1516],{"class":617}," instance",[112,1518,723],{"class":359},[112,1520,1521],{"class":129}," httpAdapter.",[112,1523,1524],{"class":416},"getInstance",[112,1526,580],{"class":129},[112,1528,1529,1531,1534,1536,1539,1542],{"class":114,"line":234},[112,1530,1465],{"class":129},[112,1532,1533],{"class":416},"useGlobalFilters",[112,1535,735],{"class":129},[112,1537,1538],{"class":359},"new",[112,1540,1541],{"class":416}," AllExceptionsFilter",[112,1543,1544],{"class":129},"(instance))\n",[112,1546,1547],{"class":114,"line":242},[112,1548,197],{"emptyLinePlaceholder":196},[112,1550,1551,1553,1556],{"class":114,"line":250},[112,1552,1465],{"class":129},[112,1554,1555],{"class":416},"enableCors",[112,1557,901],{"class":129},[112,1559,1560,1563,1566],{"class":114,"line":259},[112,1561,1562],{"class":129},"    origin: ",[112,1564,1565],{"class":133},"'*'",[112,1567,670],{"class":129},[112,1569,1570],{"class":114,"line":267},[112,1571,1572],{"class":129},"    allowedHeaders:\n",[112,1574,1575,1578],{"class":114,"line":278},[112,1576,1577],{"class":133},"      'Origin, X-Requested-With, Content-Type, Accept, Authorization'",[112,1579,670],{"class":129},[112,1581,1582],{"class":114,"line":289},[112,1583,1584],{"class":129},"  })\n",[112,1586,1587],{"class":114,"line":295},[112,1588,197],{"emptyLinePlaceholder":196},[112,1590,1591,1594,1596,1599,1601,1604],{"class":114,"line":306},[112,1592,1593],{"class":359},"  await",[112,1595,1492],{"class":129},[112,1597,1598],{"class":416},"listen",[112,1600,735],{"class":129},[112,1602,1603],{"class":617},"3000",[112,1605,741],{"class":129},[112,1607,1608],{"class":114,"line":311},[112,1609,1158],{"class":129},[112,1611,1612,1615],{"class":114,"line":319},[112,1613,1614],{"class":416},"bootstrap",[112,1616,580],{"class":129},[10,1618,1619],{"id":1619},"動作確認",[57,1621,1622],{"id":1622},"トークン取得スクリプト",[14,1624,1625],{},"Auth0からアクセストークンを取得します。",[14,1627,1628,1629],{},"参考: ",[20,1630,1633],{"href":1631,"rel":1632},"https:\u002F\u002Fdev.classmethod.jp\u002Farticles\u002Fauth0-nestjs-backend-sample\u002F",[24],"Auth0 + NestJS バックエンドサンプル",[102,1635,1637],{"className":337,"code":1636,"language":339,"meta":107,"style":107},"#!\u002Fusr\u002Fbin\u002Fenv bash\n\nauth_url=https:\u002F\u002Fyour-domain.auth0.com\nclient_id=your_client_id\nclient_secret=your_client_secret\nusername=\"user@example.com\"\npassword=\"password\"\n\necho \"🎁 id_tokenを取得中...\"\n\ncurl -s --request POST \\\n  --url ${auth_url}\u002Foauth\u002Ftoken \\\n  --header 'content-type: application\u002Fx-www-form-urlencoded' \\\n  --data grant_type=password \\\n  --data username=${username} \\\n  --data password=${password} \\\n  --data client_id=${client_id} \\\n  --data client_secret=${client_secret}\n\necho \"\\n\"\n",[109,1638,1639,1644,1648,1658,1668,1678,1688,1698,1702,1710,1714,1731,1744,1754,1764,1777,1789,1801,1811,1815],{"__ignoreMap":107},[112,1640,1641],{"class":114,"line":115},[112,1642,1643],{"class":118},"#!\u002Fusr\u002Fbin\u002Fenv bash\n",[112,1645,1646],{"class":114,"line":122},[112,1647,197],{"emptyLinePlaceholder":196},[112,1649,1650,1653,1655],{"class":114,"line":137},[112,1651,1652],{"class":129},"auth_url",[112,1654,360],{"class":359},[112,1656,1657],{"class":133},"https:\u002F\u002Fyour-domain.auth0.com\n",[112,1659,1660,1663,1665],{"class":114,"line":146},[112,1661,1662],{"class":129},"client_id",[112,1664,360],{"class":359},[112,1666,1667],{"class":133},"your_client_id\n",[112,1669,1670,1673,1675],{"class":114,"line":154},[112,1671,1672],{"class":129},"client_secret",[112,1674,360],{"class":359},[112,1676,1677],{"class":133},"your_client_secret\n",[112,1679,1680,1683,1685],{"class":114,"line":165},[112,1681,1682],{"class":129},"username",[112,1684,360],{"class":359},[112,1686,1687],{"class":133},"\"user@example.com\"\n",[112,1689,1690,1693,1695],{"class":114,"line":176},[112,1691,1692],{"class":129},"password",[112,1694,360],{"class":359},[112,1696,1697],{"class":133},"\"password\"\n",[112,1699,1700],{"class":114,"line":184},[112,1701,197],{"emptyLinePlaceholder":196},[112,1703,1704,1707],{"class":114,"line":193},[112,1705,1706],{"class":617},"echo",[112,1708,1709],{"class":133}," \"🎁 id_tokenを取得中...\"\n",[112,1711,1712],{"class":114,"line":200},[112,1713,197],{"emptyLinePlaceholder":196},[112,1715,1716,1719,1722,1725,1728],{"class":114,"line":208},[112,1717,1718],{"class":416},"curl",[112,1720,1721],{"class":617}," -s",[112,1723,1724],{"class":617}," --request",[112,1726,1727],{"class":133}," POST",[112,1729,1730],{"class":617}," \\\n",[112,1732,1733,1736,1739,1742],{"class":114,"line":218},[112,1734,1735],{"class":617},"  --url",[112,1737,1738],{"class":129}," ${auth_url}",[112,1740,1741],{"class":133},"\u002Foauth\u002Ftoken",[112,1743,1730],{"class":617},[112,1745,1746,1749,1752],{"class":114,"line":226},[112,1747,1748],{"class":617},"  --header",[112,1750,1751],{"class":133}," 'content-type: application\u002Fx-www-form-urlencoded'",[112,1753,1730],{"class":617},[112,1755,1756,1759,1762],{"class":114,"line":234},[112,1757,1758],{"class":617},"  --data",[112,1760,1761],{"class":133}," grant_type=password",[112,1763,1730],{"class":617},[112,1765,1766,1768,1771,1774],{"class":114,"line":242},[112,1767,1758],{"class":617},[112,1769,1770],{"class":133}," username=",[112,1772,1773],{"class":129},"${username} ",[112,1775,1776],{"class":617},"\\\n",[112,1778,1779,1781,1784,1787],{"class":114,"line":250},[112,1780,1758],{"class":617},[112,1782,1783],{"class":133}," password=",[112,1785,1786],{"class":129},"${password} ",[112,1788,1776],{"class":617},[112,1790,1791,1793,1796,1799],{"class":114,"line":259},[112,1792,1758],{"class":617},[112,1794,1795],{"class":133}," client_id=",[112,1797,1798],{"class":129},"${client_id} ",[112,1800,1776],{"class":617},[112,1802,1803,1805,1808],{"class":114,"line":267},[112,1804,1758],{"class":617},[112,1806,1807],{"class":133}," client_secret=",[112,1809,1810],{"class":129},"${client_secret}\n",[112,1812,1813],{"class":114,"line":278},[112,1814,197],{"emptyLinePlaceholder":196},[112,1816,1817,1819],{"class":114,"line":289},[112,1818,1706],{"class":617},[112,1820,1821],{"class":133}," \"\\n\"\n",[57,1823,1825],{"id":1824},"apiテスト","APIテスト",[14,1827,1828,1829,1832],{},"取得した",[109,1830,1831],{},"id_token","を使ってAPIをテストします。",[102,1834,1836],{"className":337,"code":1835,"language":339,"meta":107,"style":107},"# 認証成功\ncurl -i -X GET 'http:\u002F\u002Flocalhost:3000\u002Fprivate' \\\n  -H 'Authorization: Bearer \u003Cid_token>'\n\n# レスポンス例\n# HTTP\u002F1.1 200 OK\n# Content-Type: application\u002Fjson; charset=utf-8\n#\n# {\"message\":\"認証成功\"}\n\n# 認証失敗（不正なトークン）\ncurl -i -X GET 'http:\u002F\u002Flocalhost:3000\u002Fprivate' \\\n  -H 'Authorization: Bearer invalid_token'\n\n# レスポンス例\n# HTTP\u002F1.1 401 Unauthorized\n",[109,1837,1838,1843,1861,1869,1873,1878,1883,1888,1893,1898,1902,1907,1921,1928,1932,1936],{"__ignoreMap":107},[112,1839,1840],{"class":114,"line":115},[112,1841,1842],{"class":118},"# 認証成功\n",[112,1844,1845,1847,1850,1853,1856,1859],{"class":114,"line":122},[112,1846,1718],{"class":416},[112,1848,1849],{"class":617}," -i",[112,1851,1852],{"class":617}," -X",[112,1854,1855],{"class":133}," GET",[112,1857,1858],{"class":133}," 'http:\u002F\u002Flocalhost:3000\u002Fprivate'",[112,1860,1730],{"class":617},[112,1862,1863,1866],{"class":114,"line":137},[112,1864,1865],{"class":617},"  -H",[112,1867,1868],{"class":133}," 'Authorization: Bearer \u003Cid_token>'\n",[112,1870,1871],{"class":114,"line":146},[112,1872,197],{"emptyLinePlaceholder":196},[112,1874,1875],{"class":114,"line":154},[112,1876,1877],{"class":118},"# レスポンス例\n",[112,1879,1880],{"class":114,"line":165},[112,1881,1882],{"class":118},"# HTTP\u002F1.1 200 OK\n",[112,1884,1885],{"class":114,"line":176},[112,1886,1887],{"class":118},"# Content-Type: application\u002Fjson; charset=utf-8\n",[112,1889,1890],{"class":114,"line":184},[112,1891,1892],{"class":118},"#\n",[112,1894,1895],{"class":114,"line":193},[112,1896,1897],{"class":118},"# {\"message\":\"認証成功\"}\n",[112,1899,1900],{"class":114,"line":200},[112,1901,197],{"emptyLinePlaceholder":196},[112,1903,1904],{"class":114,"line":208},[112,1905,1906],{"class":118},"# 認証失敗（不正なトークン）\n",[112,1908,1909,1911,1913,1915,1917,1919],{"class":114,"line":218},[112,1910,1718],{"class":416},[112,1912,1849],{"class":617},[112,1914,1852],{"class":617},[112,1916,1855],{"class":133},[112,1918,1858],{"class":133},[112,1920,1730],{"class":617},[112,1922,1923,1925],{"class":114,"line":226},[112,1924,1865],{"class":617},[112,1926,1927],{"class":133}," 'Authorization: Bearer invalid_token'\n",[112,1929,1930],{"class":114,"line":234},[112,1931,197],{"emptyLinePlaceholder":196},[112,1933,1934],{"class":114,"line":242},[112,1935,1877],{"class":118},[112,1937,1938],{"class":114,"line":250},[112,1939,1940],{"class":118},"# HTTP\u002F1.1 401 Unauthorized\n",[10,1942,1943],{"id":1943},"まとめ",[14,1945,1946],{},"Auth0を使ってNestJSとHasuraの認証を統一することで、以下のメリットが得られます。",[30,1948,1949,1955,1961],{},[33,1950,1951,1954],{},[36,1952,1953],{},"一貫性"," 単一の認証基盤でマイクロサービス全体を保護",[33,1956,1957,1960],{},[36,1958,1959],{},"セキュリティ"," JWT検証により、改ざんされたトークンを検出",[33,1962,1963,1966],{},[36,1964,1965],{},"柔軟性"," Auth0の豊富な機能（MFA、ソーシャルログインなど）を活用",[14,1968,1969],{},"この実装パターンは、マイクロサービスアーキテクチャにおける認証のベストプラクティスの一つです。",[1971,1972,1973],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":107,"searchDepth":122,"depth":122,"links":1975},[1976,1977,1978,1982,1988,1992],{"id":12,"depth":122,"text":12},{"id":28,"depth":122,"text":28},{"id":54,"depth":122,"text":55,"children":1979},[1980,1981],{"id":59,"depth":137,"text":59},{"id":81,"depth":137,"text":82},{"id":327,"depth":122,"text":328,"children":1983},[1984,1985,1986,1987],{"id":334,"depth":137,"text":334},{"id":392,"depth":137,"text":393},{"id":1161,"depth":137,"text":1161},{"id":1360,"depth":137,"text":1361},{"id":1619,"depth":122,"text":1619,"children":1989},[1990,1991],{"id":1622,"depth":137,"text":1622},{"id":1824,"depth":137,"text":1825},{"id":1943,"depth":122,"text":1943},"2022-08-20","NestJSのAPIエンドポイントとHasuraのGraphQLをAuth0で統一的に認証する方法を解説します。Guardパターンを使った実装例とJWT検証の実践的なコード例を紹介します。","md",{"tags":1997},[1998,1999,2000,2001],"graphql","hasura","nestjs","auth0","\u002Fblog\u002Fnestjs-hasura-auth0-authentication",{"title":5,"description":1994},"blog\u002Fnestjs-hasura-auth0-authentication","zOdI1WwHXtW43qiPJSq8rhtIkkQC2i7MR7u_Kr4eeBo",1773664053974]