概要

以下のIIIF認証API 2.0の動作確認を行う機会がありましたので、備忘録です。

https://iiif.io/api/auth/2.0/

以下のようなデモサイトを作成しました。

https://iiif-auth-nextjs.vercel.app/ja

リポジトリは以下です。

https://github.com/nakamura196/iiif-auth-nextjs

以下、AIによる説明です。なお、Miradorではうまく動作させることができなかったため、今後の課題です。

概要

本記事では、IIIF Authentication API 2.0 の認証フローを、実際のHTTPリクエスト/レスポンスのレベルで詳細に解説します。各ステップでどのようなリクエストが送信され、どのようなレスポンスが返されるのかを追跡していきます。

アーキテクチャ概要

(CBlrioewnster)IIIFServerAuthService

認証フローの詳細

Step 1: 初回の画像情報リクエスト(未認証)

リクエスト:

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

処理フロー(サーバー側):

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

レスポンス:

HCA{}TocTnc""}Ptees]/esre"""1nsrr@it.t-ovcdy1-Crio"pTo"cn:e4yn:et"0pt"e":1er":xh:oAtt"Ulu["tAna-t{:puapAh:tuple"/htllnh/Phiottlrocwitoora-cpcbitOa:aezirt/lSeoii/hedngoior/inisvjnitis:rf:coe.3enqi02uo0"i/1ra/epadip"/i,a/uitihi/f2pcroonbtee"x,t.json",

Step 2: Probe Service へのリクエスト

クライアントは401レスポンスからAuthProbeService2のURLを取得し、認証状態を確認します。

リクエスト:

GHAEocTscte/:patpl:icaiapilpihlfiscptar:to3ib0oe0n1/HjTsToPn/1.1

処理フロー(サーバー側):

e}xpcci}r}aooofe,prnnci}t""""}ptss(ofu@tss]{/tttnr}rcyte""""""}aaos(e)nopar@itpls]spsatktpt""""};netvcdyrae"""tiyuoeau@tslNt"uio"pobr@ita/ntknpyrcyto""ee:scn:efevcdytiche)alnopacitxx"et"ilio"puiHnyonetadytt":"e`:l"cn:esife{laNt"ut"pR"A:x$e:et":fua=odee:si:ee:u4t{"""e`:/nda)xx"o"st0["rA:":x$4pceadtt":n`:p"h1{:euLt{"0rtru{R"A"$ohP,qt"o["rA1oit=e:u2:{"ntr"uhag{:eubo=hst0rIstoheAciqt}enHap"h0{emepbtsctn"uh)/rewohP,qa.:ettciheA;rGeaantrugj/Rp.evttscoEqdistoeese:nseottcuTuetepbs"is/es"p.et(er.:etniu/xS,I:nsers?vj/R.(iliteI/es.et.esen{ftiUrI/xTtq.rrise.2irvFitosuheiniuxi"fliiUkeepf(ilto,..cAiresaly{ftU/ioeuflntdaT.2raor2t..S:ecoi"lp/i"hioereko,.iag,orrNs(e/o/piD/ive.'naraineagixgB(piu/}mpicteetigta/oineRtao/ihua"/}2e(rkan/tp,a/"q'eeu}2hiuauarnt////tpeu)hac2ihist';/po/i//th,2inci2i)o//tof/ircien/ci{ioixtaofz)nitecn/a;tf.xcttte/jteeoixis.sxkotmojsten.ans`.n'jg"o,j`)se,ns,;o/"ons,n"a",m,ple/info.json`,

レスポンス(未認証):

{}""""}@tss]cyte""""""}opar@itpls]netvcdyrae"""t"uio"pobr@ite:scn:efevcdyx"et"ilio"pt":"e":l"cn:e"A:xhe:et":u4tt"""e":t0["tA:":xh"h1{:puLtt"hP,:t"o["tAtr"/hag{:putoh/Aci:tpbtlctn"/h:etocih/A/Rpcevttlc/e:aseotocis/ls"pceiu/hS,I:asilioeI/lsftisrI/hT.2itvFiooi"f:iisko,.3cAite/i0euf:nao02t.3Sp/1"hi0eia/,o0r/paD/1aipea/iu/impacta/oipehui"/i2/ti,a/"2hiui//ftic2hioa/fncc2toctenecoxtsoktesne.x"tnjt,e"s.x,ojtns."oj,ns"o,n",

Step 3: 認証ウィンドウの開始

クライアントはProbe ServiceのレスポンスからAuthAccessTokenService2のURLを取得し、ポップアップウィンドウを開きます。

クライアント側の処理:

ccccttc)ooooooo;nnnnkknt''sssseesoiwttttnntkiiUUeidatmtrranftuoeolluU-htkIsk..tra=heDsesshlu6SnaneeW.t0eSgUUaaith0reeRrrrno',vrILlccdS,hivdhhoteci=PPwriec=aaigenrr=nh=ceaagt=rwmmw(=pyssi)6rapU..n,0outRssd0btoLeeo'eh.(ttwRSrt((.eeao''osrnkmopuvdeerelionsintcmSsg(.eUeais.UrgnesIve'reDiI,vr(cdiv)e'wci;.,iecin[edmd0[)eo]0;sw;]s.;algoecIadt)i;on.origin);

生成されるURL:

http://localhost:3001/api/iiif/token?messageId=60f4420d-52c1-48ae-a24f-c3bb948fa0dc&origin=http://localhost:3001

Step 4: Token Service のリダイレクト処理

リクエスト:

GHEoTst/:apliciailihfstto:k3e0n0?1messageId=60f4420d-52c1-48ae-a24f-c3bb948fa0dc&origin=http://localhost:3001HTTP/1.1

処理フロー(サーバー側):

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

0

レスポンス:

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

1

Step 5: ログインページでの認証

ログインフォーム送信:

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

2

処理フロー(サーバー側):

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

3

JWT生成の詳細:

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

4

レスポンス:

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

5

Step 6: トークンの伝達(postMessage)

ログイン成功後、クライアント側でトークンを受け取り、Token Service経由で元のウィンドウに送信します。

クライアント側(auth/page.tsx):

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

6

Step 7: 元のウィンドウでのトークン受信

クライアント側(メインウィンドウ):

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

7

Step 8: 認証済みでの画像リクエスト

リクエスト:

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

8

処理フロー(サーバー側):

GHAEocTscte/:patpl:icaiapilpihlfiscitam:ta3ig0oe0n/1/sjasmopnle/info.jsonHTTP/1.1

9

レスポンス:

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

0

トークンの永続化と同期

localStorage による永続化

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

1

タブ間の同期

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

2

CORS設定

Image API エンドポイント

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

3

Probe/Access Service

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

4

エラーハンドリング

トークン有効期限切れ

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

5

認証エラーのハンドリング

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

6

セキュリティ考慮事項

1. トークンの署名検証

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

7

2. HTTPS の使用(本番環境)

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

8

3. Origin の検証

e}xpcli}ci}aoefofprnttnr}pt1s2(o3s4(e,/.tt.!k.t.!tes}aaoteiure]{psAakonisrrr"""iyuueksVnov@its/nttne=VaricdytichhnalN:co"paioH=)rlieen:etifreeidx':t"ufuiaa{qd)tAe`:s/nzduu4Ru[x$:icaete=0{et{t{"mttrhs1sh"rA4aiiHttpe:eu0goo=e.oonqt1ennanknt"uh/rdeesiheP}[Geexnectsr)iEqrt.atto;dTu?U?jtp.b](e.rsi:ne/rsrlaoo/eSiete.wnn/xenq.psa(itrfuhlei{riUvoeeaateiri.sacrqflcjtdecvu..es:e(heiio2or'Prror"nNsBaie/i/e.erfdagrxgaay'piotermT,inuRteso/}te(r.ka/eq'geua.ua'entpteu,t(hisst(t//th'o2i)otk/ir)oeci{i;knofze)n/antpt':eri)xoontbnu.e'lj`)ls,;;o'n;",

9

パフォーマンス最適化

1. トークンのキャッシング

HCA{}TocTnc""}Ptees]/esre"""1nsrr@it.t-ovcdy1-Crio"pTo"cn:e4yn:et"0pt"e":1er":xh:oAtt"Ulu["tAna-t{:puapAh:tuple"/htllnh/Phiottlrocwitoora-cpcbitOa:aezirt/lSeoii/hedngoior/inisvjnitis:rf:coe.3enqi02uo0"i/1ra/epadip"/i,a/uitihi/f2pcroonbtee"x,t.json",

0

2. 並列リクエストの処理

HCA{}TocTnc""}Ptees]/esre"""1nsrr@it.t-ovcdy1-Crio"pTo"cn:e4yn:et"0pt"e":1er":xh:oAtt"Ulu["tAna-t{:puapAh:tuple"/htllnh/Phiottlrocwitoora-cpcbitOa:aezirt/lSeoii/hedngoior/inisvjnitis:rf:coe.3enqi02uo0"i/1ra/epadip"/i,a/uitihi/f2pcroonbtee"x,t.json",

1

トラブルシューティング

1. ポップアップブロッカー

HCA{}TocTnc""}Ptees]/esre"""1nsrr@it.t-ovcdy1-Crio"pTo"cn:e4yn:et"0pt"e":1er":xh:oAtt"Ulu["tAna-t{:puapAh:tuple"/htllnh/Phiottlrocwitoora-cpcbitOa:aezirt/lSeoii/hedngoior/inisvjnitis:rf:coe.3enqi02uo0"i/1ra/epadip"/i,a/uitihi/f2pcroonbtee"x,t.json",

2

2. postMessage の受信失敗

HCA{}TocTnc""}Ptees]/esre"""1nsrr@it.t-ovcdy1-Crio"pTo"cn:e4yn:et"0pt"e":1er":xh:oAtt"Ulu["tAna-t{:puapAh:tuple"/htllnh/Phiottlrocwitoora-cpcbitOa:aezirt/lSeoii/hedngoior/inisvjnitis:rf:coe.3enqi02uo0"i/1ra/epadip"/i,a/uitihi/f2pcroonbtee"x,t.json",

3

まとめ

IIIF Authentication API 2.0 の実装では、以下の流れでリクエストが処理されます:

  1. 初回アクセス → 401 with Probe Service
  2. Probe Service → 401 with Access Service
  3. Token Service → Login Page redirect
  4. Login → JWT Token generation
  5. postMessage → Token delivery to main window
  6. Authenticated Request → Protected resource access

各ステップで適切なエラーハンドリングとセキュリティ対策を実装することで、安全で使いやすい認証システムを構築できます。

参考資料