はじめに

研究データ管理基盤「GakuNin RDM」と Next.js アプリケーションを OAuth2 で連携する方法を解説します。GakuNin RDM は OSF(Open Science Framework)互換の API を提供しているため、OSF の OAuth2 フローを参考に実装できます。

本記事では、next-auth を使用した実装方法と、アクセストークンの自動リフレッシュ というハマりポイントについて詳しく説明します。

GakuNin RDM とは

GakuNin RDM(Research Data Management)は、国立情報学研究所(NII)が提供する研究データ管理サービスです。

研究者が研究データを安全に保存・共有・公開できるプラットフォームで、学認(GakuNin)認証との連携により、日本の大学・研究機関のユーザーが利用できます。

事前準備

1. OAuth アプリケーションの登録

GakuNin RDM の設定画面から OAuth アプリケーションを登録します。

  1. https://rdm.nii.ac.jp/settings/applications/ にアクセス
  2. 「Developer application を登録する」をクリック
  3. 以下を設定:
    • Application name : アプリ名
    • Application homepage URL : http://localhost:3000(開発時)
    • Application description : 説明
    • Authorization callback URL : http://localhost:3000/api/auth/callback/gakunin

登録後、Client IDClient Secret が発行されます。

2. 環境変数の設定

#GGNNOAAEES.KKXXFeUUTT_nNNAASvIIUUC.NNTTOl__HHPoCC__EcLLUS=aIIREolEELCsNN=RfTThE.__tTfISt=uDEpyl=C:lyRu_oE/rruTl_er=ora_ycadcoanluldoirhose_omfncs_.tltsf_i:euie3cldn0rlt0e__0twsreictreet

next-auth でのカスタムプロバイダー設定

GakuNin RDM は next-auth のビルトインプロバイダーには含まれていないため、カスタムプロバイダーとして設定します。

基本設定

ie}mx;ppp]loor,irro{}bttv,/iintcca}t}up}atcddayllu,o,sr,uyoe:mpiitup}kua}eor}tpnreeeehra,ers,rfe;hess"::nnolrcsrrnlyc}c)ci}riitine.t:gttr:alcee::no)o;ofenludamt{a""ISimiosdcncccgr;n"{}nttfer:masa[kGodez"seppi{"slloreshs(huo(neiNuuaa:cah:neorhrtiidadttmh}bt!rr:pp:letnkurttt:neteeeenitee,oronr{r:xhiutpeit{_sctqbnn:trrpta"djew"ooptOnNhrtopipetpuott_eeshdCyss{hffrpAp"i"o:nsdr__sed__ctcs:oeo,o.ntiiorut,n,c:::otu:syisoyt/drnnoettllfotiep/cyr/tdenp_=/:stkwopeeifhoRsr{/pepi/(=:cteua:e=)ks).liOnDsoarse:acre:rac"nEe:delpsM.ccos:conpexiwcP{ta{rn/{a.et:"eecc.`cnertt":aoO-wrs/td.i,nsoee"$otwo:.aiuSTao:aaadoNvsusnc{uecpu`tnTyirp.tane..nsvopnxUepat$t"pt(jiiatsxGet..drttRsrrh{fs,e`s.d.atAnseOeos)Lsoaope."rTor,a.}AKv.nS"c.S.cmrrtr:eondtauU.rvF,er{eeesiocdskmttftNGd._sdans.zchm".e}.rtrhIAmGSsmrvscae(.ajn;niroONK.AC..c..otsnpsibimp_UnKOenhGedisiporiubtCNiUPniPAneo.ilne.tu"iLIiNEviaKvne.i(qaetnoIN.I..rU.a_nac)ucseenE_aNNaaNGscvca;e..sxsNCc_EcmIAo..tsjf.tTL.CX.sNKsdNjitpue-=_IjL"Tj(_UteEpo/lmaIEpIoAp{CNr"X/nfvlau{DN/EsU/LIi,To/a2_it,ToNfToINnAaxi/nlh_aT.HaE_gUu-lua,"Su_f_uNC,Ttwesm;EtIuUtTLHhwdeeChDlRh_I_2w:r,R2,lL2IEU/-sE/_}/DNRtf$/Tar/t!TLoo{m,ueao,_}krretapkS/eme/hdieEan-s"o/nCp"u.,roa"Ri,rsisu,E/ltzftTaeae.h!unt"f/,tcu,uchosla/dTllcee_ladxwbl"tral,}icb`tka)e/c;"gk,a/kguankiunn`i,n`,

ポイント

  1. token.request のカスタマイズ : GakuNin RDM は application/x-www-form-urlencoded 形式でのトークンリクエストを期待するため、カスタムリクエスト関数を実装
  2. userinfo のパース : OSF API は { data: { id, attributes: { ... } } } という構造でレスポンスを返すため、profile 関数でマッピング

ハマりポイント:トークンの自動リフレッシュ

問題

OAuth2 のアクセストークンには有効期限があります(GakuNin RDM では約1時間)。

next-auth のデフォルト実装では、トークンのリフレッシュは行われません 。そのため、ログイン後しばらくすると、以下のようなエラーが発生します:

Failedtofetchprojects:401-{"errors":[{"detail":"UserprovidedaninvalidOAuth2accesstoken"}]}

ユーザーはログイン済みなのに、API が 401 エラーを返す、という混乱を招く状況になります。

解決策

JWT コールバックでトークンの有効期限を管理し、期限切れ前に自動リフレッシュを行います。

d}d}a}e}eesx;ci}ci}yt}}pc}llnlnnraa,iataeataraecyc}c)ci}r}cr}urla}a}brecrrececro)o;ofe;ae;ttls,s,/ercrercfcrf{nccrg;n"{}nttaarttehbyi}i}ryssrafeofereousllershs(huccecurOcanffeneeeumasrmasesrntiifattmh}bt!rr.ccfhr.rpoccr}rtcssttocs?ocsss?ceerntee,oronteerntotnpk(e;(eussuhdeT:deThT:tbnnetrpta"drewosse(orisrsjatarauttrsiir.uouoToiotts_eshdCyes{kssse{k:oto:wcucecsouneoontlSkslJkoksod__hts:oeo,fpneTThrenvtcr.cfcekrsnnseeeteWeketnyis_ypdrnroenooTrn"sai{(ontererenrs..ssnrTnenrdetp/:stenw,kkoo,Rud{uosesIneiaee"s?i"?nEir=:coena:esseekreten{ksssd.tfocrsni:nn{:?xnerk:sc"nheEnne)fhrtteThT:aorncrseoge:pgfnpeeecP{te.r:EnrcOso)noTocke(eoixns;xsi;rertn"oO-dorx:{eapk,kokuces{srottttsrewo::r=uSTTkorpslte{ekesenhsn-{r-rtescenTyo)reirhlinnenes;AsT=;aiairshUeptfat"pk(freAbo,:nErsceoununi?ARsrorws,ee{"refcan:x?6Tcskttgtgn:cLsokea."nResrccsaap.0oeseoh;h;gcS.cesir:ses:eek:ccaiiksink"/;neeeenhtdfhssscccrdesoejusans._m"=reDhsNooce,nTn=n{wmsrvsrtf.aedaeTeuuosEo,.tbTc..eoenpasTtdoxnnu:xkte"eohGefktipwhoeTktttnpetorrkPAnrecilaAk.oeA,.tDinokr{;eaKvenh.iicenknua.ar(keonrU.s"(actcnoeEtucrtetenr(aNGh,caeswnrhsceeson.;tmIAT.trs.(srOeef.kaosNKojiesa).oprsrn&e}ck(_UkposTcrrtseo&n)ce{CNe/npoc+e"i}_sw)enLIno/okef,o)th(D;{s:IN!axnesrrno_)asE_,u-snsees{kttTJNCtweE_fseoeoWTLhw.rtrh=nk.kT_I2wjroe_,e(ne)IE/-sokst{naonDNtforeho,cw;{!Toon"nekc(,_kr(),deo)Sem);TnuEn-;on<C"uk?tR,re?.tElneoTestxk!n.ope,cekinoxer.dpneaei.scdrr_c"eeie,sfns_rsieaTnssohkTneoun1kmE0ebx0nep0,ri,)res1-00600,000){

フロー図

aarcceccfoerusYNenNseostoTshoAkcecneEsxspTYioerkseesn(>>)JWnTo>w>C---ta+olaraklcec6ebcfc0naerestecsesorkssskrThTeooTonrkokekenennExpires

クライアント側でのエラーハンドリング

リフレッシュトークンも期限切れの場合、session.error"RefreshAccessTokenError" がセットされます。これを検知して再サインインを促すことができます。

"iie}ummxspppcu}rceoooos,eorrrnei}tmctttsEf[upltfssroi{{ff(ienneu{esgsenuuncensntsscdtsIi>t"eeta(sno{s;SEit(i(nc/efoa)o"]hSsfn:ng)iese=?a;lsicSs>.kdsoteeeurinss{rneo,}ssrinniion}Gsfoor"<uirnn)/agoG=;>rnmu}=;dIa=.n"r=trd"s}e(uRxa{sefcefrtcSro"heem;isslsh"diAnroceencxn(et)s-};sa:Tuot{khe/cnrhEeiralrcdotrr"e";n):{React.ReactNode}){

API での使用例

iiie}mmmxppppci}c}craooooofo)oeprrrrnr)nh};ntptttts(e;se,su/t!t{{taAtra{{{asudunpsseresretdiNgayesnrterhaN/eeunssrassoterxttcsiNotp:raxdtShioeruoitmReOfonx:sn{z=R/erpun?t:saepsvtn.R"etasrpeic=aeN4iwpoorotcso0a=oaojnSniacpt1cninesesoweoca:tscesnasna}ewets}issusa`r.s}iGtTetsiBej/ofEo.hTtessrfnrTgkjeoapooro(eesnkfronuo}m)tnoteeen(tmS)nintrsdef"{e(ccea."r@r{ah$.ttno/vt使({jasemlee"ss)xirdheo;t"bS"tsn/n/ets(seas}pi)exus,so;rtti:nv-ho/.ea"n/aru;(ac"tapc;huie"t.s;hrsOdTpmot.kineoinni}s.`)a,;c.jp/v2/users/me/nodes/",{

まとめ

GakuNin RDM と next-auth を連携する際のポイント:

  1. カスタムプロバイダー として設定が必要
  2. トークンリクエストapplication/x-www-form-urlencoded 形式
  3. アクセストークンの自動リフレッシュ を実装しないと、ログイン済みでも 401 エラーが発生する
  4. リフレッシュトークンも期限切れ の場合に備えて、クライアント側でエラーハンドリングを実装

特に3番目のトークンリフレッシュは見落としがちなので、OAuth2 連携を実装する際は必ず考慮しましょう。

参考