Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHP で学ぶ OAuth 入門

SAW
April 12, 2025

PHP で学ぶ OAuth 入門

PHPカンファレンス小田原2025 の発表資料です。

SAW

April 12, 2025
Tweet

More Decks by SAW

Other Decks in Programming

Transcript

  1. $(whoami) w ࢯ໊Ճ౻फҰ࿠ ࡀ  w ϋϯυϧωʔϜ4"8 w 9 چ5XJUUFS

    !B[VLJ@FBUFS w ؔ੢ͷ*5ΤϯδχΞίϛϡχςΟͷ೐΍͔͠ ࣗশ  w େࡕࡏॅɾѪ஌ग़਎ w ಘҙ෼໺8FCΞϓϦέʔγϣϯ։ൃ w -BSBWFM 7VF w ॴଐ༗ݶձࣾΞϦ΢ʔϓ 2 چ+*4ϚʔΫ ⁴ ͕ ਓͷԣإʹݟ͑ͨͷ͸ ࣗ෼͚ͩͰ͸ͳ͍͸ͣ ࠓ೔ͷ໎ݴ
  2. ඵએ఻ w 1)1ΧϯϑΝϨϯεؔ੢ਆށͰ։࠵ w ೔෇ ۚ ɾ ౔  w

    ։࠵৔ॴਆށӺલݚमηϯλʔ w ઈࢍϓϩϙʔβϧืूத w క੾ ೔ ·Ͱ w εϙϯαʔ΋ืूத 3 ϓϩϙʔβϧืूϑΥʔϜ εϙϯαʔืूࢿྉ
  3. ͜ͷτʔΫͷ֓ཁͱ໨ඪ w ೝՄٕज़ͷͭͰ͋Δ0"VUIͷجૅΛઆ໌ w ೝূɾೝՄͷجૅ w 0"VUIͱ͸Կ͔ w 0"VUIͷ࢖͍Ͳ͜Ζ w

    ؆୯ͳ࣮૷ྫΛަ͑ͨ0"VUIͷೝՄॲཧͷઆ໌ w ໨ඪ0"VUIͷجૅతͳॲཧͷྲྀΕΛ௫Ή w ʮখాݪͰΈΜͳͰֶ΅͏0"VUIʯˠʮখాݪͰجૅΛֶΜͩ0"VUIʯ 4
  4. ೝূͱೝՄ w ೝূ "VUIFOUJDBUJPO  w γεςϜʹొ࿥͞Ε͍ͯΔϢʔβʔͷຊਓΛ֬ೝ͢Δॲཧ w ྫ4/4΍&$αΠτͳͲͷ8FCγεςϜͷϩάΠϯ w

    γεςϜʹొ࿥͞Ε͍ͯΔϢʔβʔ৘ใͱ߹க͢Δ͜ͱͰγεςϜ͕ར༻Ͱ͖Δ w ೝՄ "VUIPSJ[BUJPO  w γεςϜͷػೳ΍Ϧιʔε΁ͷΞΫηεͷՄ൱Λ੍ޚ͢Δॲཧ w ྫ6/*9ͷϑΝΠϧγεςϜͷύʔϛογϣϯ w ϑΝΠϧ΁ͷૢ࡞͕ͦͷϢʔβʔʹରͯ͠ڐՄ͞Ε͍ͯΔ৔߹ʹͷΈૢ࡞͕Մೳ 8
  5. 0"VUIͷར༻ྫ w ESBXJP͔Β(PPHMF%SJWFʹΞΫηε w ESBXJP8FC্Ͱ࡞ਤͰ͖ΔαʔϏε w (PPHMF%SJWF(PPHMF੡ͷΫϥ΢υετϨʔδαʔϏε w ESBXJP͕(PPHMF%SJWFʹٻΊΔΞΫηεݖͷྫ w

    ετϨʔδ಺ͷϑΝΠϧͷಡΈࠐΈ w ετϨʔδ΁ͷϑΝΠϧͷอଘ 10 ESBXJP͔Β(PPHMF%SJWF΁ͷ ΞΫηεͷೝՄΛཁٻ (PPHMF%SJWFͰΞΫηεݖͷೝՄΛ֬ೝ
  6. 0"VUIʹ͓͚Δొ৔ਓ෺ w ΫϥΠΞϯτ $MJFOU  w อޢର৅ϦιʔεʹΞΫηε͢ΔSEύʔςΟ੡ͷιϑτ΢ΣΞ w อޢର৅Ϧιʔε 1SPUFDUFE3FTPVSDF

     w Ϧιʔεॴ༗ऀ͕ΞΫηεݖΛ࣋ͭػೳ΍σʔλͳͲͷϦιʔε w Ϧιʔεॴ༗ऀ 3FTPVSDF0XOFS  w อޢର৅Ϧιʔε΁ͷΞΫηεݖΛ΋ͭϢʔβʔ w ೝՄαʔόʔ "VUIPSJ[BUJPO4FSWFS  w Ϧιʔεॴ༗ऀ͕ΫϥΠΞϯτΛೝՄ͢Δ࢓૊ΈΛఏڙ͢ΔγεςϜ 13
  7. ΫϥΠΞϯτͷೝՄཁٻ w Ϧιʔεॴ༗ऀΛೝՄαʔόʔʹϦμΠϨΫτ w ϦμΠϨΫτ࣌ʹೝՄର৅ͷΫϥΠΞϯτͷ৘ใΛΫΤϦύϥϝʔλʹ෇༩ 30 $query = http_build_query([ 'response_type'

    => 'code', 'client_id' => env('CLIENT_ID'), 'redirect_uri' => 'http://localhost:8000/callback', 'scope' => 'file.read file.write', ]); $authEndpoint = Uri::new('http://localhost:8001/authorize') ->withQuery($query); return redirect()->away($authEndpoint); ೝՄαʔόʔ Ϧιʔεॴ༗ऀ ΫϥΠΞϯτ ೝՄαʔόʔʹ ϦμΠϨΫτ localhost:8000 localhost:8001 ෇༩ํࣜ͸BVUIPSJ[BUJPODPEF ΫϥΠΞϯτ͕ཁٻ͢Δείʔϓ
  8. ΫϥΠΞϯτͷೝՄͷ֬ೝ w ΫϥΠΞϯτΛೝՄ͢Δ͔Ϧιʔεॴ༗ऀʹ֬ೝ w είʔϓ͸ۭനจࣈ۠੾ΓͰΫΤϦύϥϝʔλͰ౉͞ΕΔ 31 $clientId = $request->query('client_id'); $client

    = Client::find($clientId); // 要求されているスコープを取得 $scope = str($request->query('scope')) ->explode(' ')->filter(); return view('authorize', [ 'client' => $client, 'scope' => $scope, ]); ೝՄαʔόʔ Ϧιʔεॴ༗ऀ ΫϥΠΞϯτͷ ೝՄΛ֬ೝ localhost:8001
  9. ΫϥΠΞϯτͷೝՄ w ೝՄίʔυΛൃߦͯ͠ΫϥΠΞϯτͷίʔϧόοΫ63-ʹϦμΠϨΫτ w ೝՄίʔυͱϦιʔεॴ༗ऀ͕ڐՄͨ͠είʔϓΛΫΤϦύϥϝʔλͱͯ͠෇༩ 32 $scope = $request->input('scope'); $authzCode

    = Str::random(); $codes[$authzCode] = ['scope' => $scope]; Cache::put('codes', $codes); $callbackUrl = Uri::new($redirectUrl) ->withQuery([ 'scope' => $scope, 'code' => $authzCode, ]); return redirect()->away($callbackUrl); ೝՄαʔόʔ Ϧιʔεॴ༗ऀ ΫϥΠΞϯτ ΫϥΠΞϯτʹ ϦμΠϨΫτ localhost:8000 localhost:8001 ೝՄίʔυΛ෇༩
  10. τʔΫϯͷऔಘ w ΫϥΠΞϯτ͸ೝՄαʔόʔʹೝՄίʔυΛૹ৴ w ΫϥΠΞϯτ͸#BTJDೝূͰࣝผ w ೝՄαʔόʔ͸ΫϥΠΞϯτʹΞΫηετʔΫϯΛൃߦ 33 ೝՄαʔόʔ ΫϥΠΞϯτ

    ೝূ৘ใͱೝՄίʔυΛ ૹ৴ localhost:8000 localhost:8001 $clientId = $request->getUser(); $clientSecret = $request->getPassword(); $codes = Cache::get('codes'); $code = $codes[$request->input('code')]; // クライアントの情報と認可コードを検証 $token = Str::random(); AccessToken::create(['token' => $token]); return response()->json([ 'access_token' => $token, 'token_type' => 'Bearer', 'scope' => $code['scope'], ]); ΞΫηετʔΫϯΛૹ৴ #BTJDೝূͰΫϥΠΞϯτΛࣝผ
  11. อޢର৅Ϧιʔε΁ͷΞΫηε w ΫϥΠΞϯτ͸อޢର৅ϦιʔεʹΞΫηετʔΫϯΛར༻ͯ͠ΞΫηε w ΞΫηετʔΫϯ͸#FBSFSτʔΫϯͰ౉͞ΕΔ w อޢର৅Ϧιʔε͸ϦιʔεΛΫϥΠΞϯτʹฦ٫ w ೝՄ͞Εͨείʔϓ֎ͷΞΫηε͸ڐՄ͠ͳ͍ 34

    ΫϥΠΞϯτ ΞΫηετʔΫϯΛ෇༩ͯ͠ ϦιʔεʹΞΫηε localhost:8000 localhost:8002 Ϧιʔεͷ಺༰Λฦ٫ อޢର৅Ϧιʔε $token = $request->bearerToken(); $accessToken = AccessToken::whereFirst('token', $token); if ($accessToken?->scope->contains('file.read')) { $content = Storage::json('data.json'); return response()->json($content); } else { return response()->json(null, 403); } #FBSFSτʔΫϯ͔Β ΞΫηετʔΫϯΛऔಘ ΞΫηετʔΫϯʹཁٻ͞ΕΔ είʔϓ͕ଘࡏ͢Δ͔νΣοΫ