22 Dec

OAuth 2.0 Apigility módra


Habár legutóbbi cikkemben a Laraveles Passportot ígértem, gondoltam teszek egy próbát az Apigility átlal biztosított OAuth2.0-ás authentikációval is, részben azért, mert a B. Shaffer féle Oauth2 csomagot használja, amivel már korábban volt dolgom. Akkor überszívás volt mindezt rendesen életre lehelni, vagy legalábbis a rendszer nem éppen illett bele az egészbe, sok helyen kellett átszabni a protokollt (sokan ezért is szidják az OAuth 2.0-t a SAML-al szemben, ugyanis nagyon sok lyuk van a specifikációban, ahol többféle implementáció fordulhat elő, ami miatt lehet, hogy két fél megfelel a protokoll minden követelményének, mégis inkompatibilisek lesznek), így hát kiváncsi voltam, lehetséges-e mindezt úgymond összekattintgatni. Előljáróban annyit, hogy azért nem volt olyan egyszerű, mint hittem :)



Akkor most kellene előhalászni azt a csomagot, amit összeraktunk az első API-s cikk során, mert onnan folytatjuk a dolgot, habár az csak a teszteléshez lesz szükséges. Ha nincs meg, az sem baj, itt egy tömörített verzió, de vigyázat ezt már tegnap megsütöttem. Ha behozzuk az admin felületet, akkor az Authentication fülön tudunk új hitelesítési adaptert hozzáadni az oldalhoz:



A felugró popupnál választhatunk több módszer közül, mi most maradjunk a rendes mysql-es verziónál (habár optionalnek tűnhetnek a mezők a placeholderekkel, sajna mindegyik required):

Ha ezzel megvolnánk, akkor azt hinnénk, hogy kész is minden, ámde okkal használ adatbázist a drága, viszont a migrációt lefuttatni bizony smafu, úgyhogy keressük elő kézzel a

vendor/zfcampus/zf-oauth2/data/db_oauth2.sql

fájlt, majd importáljuk az adatbázisunkba.

Válasszuk ki a korábban készített service-ünket és rendeljük hozzá az authentikációt:



Toljunk rá egy mentést és láss csodát!... nem történt semmi, ugyanis az egyes resource-oknál meg kell határozni, hogy mely HTTP methodokra akarjuk ráereszteni az authentikációt, amit itt beállítottunk. Nézzük először a bejegyzéseket:



Ez nem egy szokványos módszer, ugyanis általában a módosítást szokták jogosultsághoz kötni, de most a teszt kedvéért ez a legegyszerűbb, így nem futhatnunk 422-es errorba és hasonlókba. Ha most meghívjuk POSTmanből az endpointot, akkor biza csúnya 403-at kapunk:



A rendszer annyiban segít, hogy idéz nekünk egy passzust a bibiliából, azonban az nekünk nem sokat segít. A 403-at biza azért kaptuk, mert nem küldtünk Authorization headert. Hát persze, hogy nem, hiszen még nem kaptunk tokent. Na de mégis hogy kapunk majd?

Na itt jönnek elő az előző cikk emlékei. Mi most egy 2-legged Oauth-ot szeretnénk, amihez első lépésben kell egy ún. client_id és client_secret. Ezeket pedig az OAuth-nál kell felregisztrálni. Igen, jól sejted, a migrációs scriptben voltak ilyen táblák. Igen, azt is jól sejted, hogy most az tök üres. Nem, nincs rá az Apigility-ben CLI tool, amivel fel tudnánk ezt venni, így kézzel kell beoktrojálni azt:
INSERT INTO `oauth_clients` (`client_id`, `client_secret`, `redirect_uri`, `grant_types`, `scope`, `user_id`) VALUES
('testclient', '$2y$10$RnnxbJ4ix9pfLjDfD0xxUungjKBIZcX.NqlWUB9MnHvYuDXeycZ2e', 'http://apigility.localhost.hu/oauth/receivecode', NULL, NULL, 'testuser');

Egy sort kell felvenni, amiben megadtuk, hogy a kliensünk id-je a testclient lesz, a secret az alábbi bcrypt-el hashelt testpass, a redirect_uri, amire visszaredirektál bennünket az authorizációs kóddal pedig a fenti URL, ami az apigility defaultja. Ha nem csak teszteljük, akkor ebből kell majd sajátot írnunk például, mert ez csak szimplán kiírja a kódot, amit utána még más infókkal vissza kell POST-olni, de ne rohanjunk előre.



Felvettük ezeket (értelemszerűen aki PHP szerverrel futtatja, az a localhost:8888 hostra mutató linket adjon meg itt), mi a következő lépés? Hát kérjük el az OAuth-tól a kódot:
http://apigility.localhost.hu/oauth/authorize?client_id=testclient&response_type=code&state=xyz&redirect_uri=http://apigility.localhost.hu/oauth/receivecode

Lőjük meg a fenti URL-t böngészőben. A következő csodás kép fog fogadni:



Hát persze, hogy engedélyezzük! Ha elfogadtuk, akkor vissza leszünk redirectálva a megadott URL-re, ahol ez fogad:



Ezek ugye default tesztoldalak, szóval ne így képzeljük majd el az éles használatát, viszont ki tudjuk próbálni a flow-t, tehát amennyiben szerveroldali alkalmazásunk van, akkor az előző oldalra nem engedünk oda egyből bárkit. Ne feledjük az auth szerver az egyetlen, ahol a felhasználók be tudnak lépni felhasználónév/jelszó párossal, tehát a login oldalak ott lesznek (megelőzve a fenti authorize request oldalt). Térjünk azonban vissza a fenti válaszra. Láthatjuk, hogy itt vissza lettünk irányítva, megkaptuk a kódot. Ha ezt mi írtuk volna meg, akkor már küldenénk is a háttérben egy POST kérést backendről, a leírtaknak megfelelően és az alábbi választ kapnánk:
{"access_token":"f48a73fba306dac07c6a694fadcbe25c2ca1593f","expires_in":3600,"token_type":"Bearer","scope":null,"refresh_token":"a9f4b7f477be8aeb46155991bdd51d7c6516e23a"}

Láthatjuk, hogy kaptunk egy access_token-t, refresh_token-t, amik 3600 másodperc, azaz kerek egy óra alatt lejárnak. A scope jelen esetben null, vagyis nem rendeltük hozzá semmihez a tokent, de lévén az apigility felületén nem tudjuk megadni, hogy mely resource-ok mely scope-okhoz tartoznak, így ez jelen esetben tárgytalan. Viszont tegyünk egy próbát a fenti tokennel és nézzük meg most mi is a válasz az alábbi kérésre:
curl -X GET -H "Accept: application/json" -H "Authorization: Bearer f48a73fba306dac07c6a694fadcbe25c2ca1593f" \
 "http://apigility.localhost.hu/posts"

Láthatjuk, hogy a headerbe bekerült a token, sima GET kérés és bumm, csak úgy dőlnek felénk a bejegyzések :)



Viszont a fenti megoldás sajnos nem igazán jó, ha JavaScript alapú alkalmazást akarunk, de szerencsénkre ezen még tudunk segíteni.

Ahhoz, hogy ez működjön be kell kapcsolnunk az ún. implicit grant_type-ot a config/autoload/local.php-ben:
return [
 'zf-oauth2' => [
 // ...

 'allow_implicit' => true,
 // ...

 ],
];

Az implicit grant type azért fontos, mert lévén frontenden vagyunk, nem tudjuk biztonságosan tárolni a client_secret-et. A kérés, amit indítunk nagyban hasonlít majd az authorizációs kódoshoz, azzal a különbséggel, hogy itt rögtön a tokennel tér vissza a szerver, amit rögtön használatba is tudunk majd venni. Persze mielőtt azt hinnénk, hogy ezáltal minden biztonság oda, egy sima client_id-t meg kibányászunk az oldalból, azt ne feledjük el, hogy itt is van egy redirect_uri, ami korlátozza a visszaéléseket.

A kérés így néz ki ebben az esetben:
curl -X GET "http://apigility.localhost.hu/oauth/authorize?response_type=token&client_id=testclient&redirect_uri=http://apigility.localhost.hu/oauth/receivecode&state=xyz"

Ami kérés egy ugyanolyan authorize oldalat eredményez:

Az allow-ra kattintva pedig redirektál minket ide:
http://apigility.localhost.hu/oauth/receivecode#access_token=c59315b201ffc917e9fb4e7a8a52e4ef699072c4&expires_in=3600&token_type=Bearer&state=xyz

A tokent megkapjuk hashmark paraméterként, amit kliensoldalon az alábbi kóddal tudjuk kiszedni (persze léteznek erre jobb módszerek, nyílván :D):
// function to parse fragment parameters

var parseQueryString = function( queryString ) {
 var params = {}, queries, temp, i, l;
 // Split into key/value pairs

 queries = queryString.split("&");
 // Convert the array of strings into an object

 for ( i = 0, l = queries.length; i < l; i++ ) {
 temp = queries[i].split('=');
 params[temp[0]] = temp[1];
 }
 return params;
};
// get token params from URL fragment

var tokenParams = parseQueryString(window.location.hash.substr(1));

Ezután a tokent már csak a korábbiakban is említett módon bele kell fűzzük az Authorization headerbe és bumm, így lett a chocapic!

Viszont hiába a kétféle megközelítés, továbbra is felmerülhet bennetek a kérdés, persze joggal, hogy mégis hogy jön itt képbe a user, azaz a resource owner, mert a példák egyikében sem láttunk egyetlen helyet, ahol a korábban emlegetett username/password-ot kellett volna beírni. Ez teljesen valid, mivel a fentiek arra az esetre érvényesek, mikor nincs külön user, hanem a resource owner egyben a kliens. De mégis kérem hol van ilyen példa?

Vegyünk pl. egy Google Cloud Messaginget (ami időközben átalakult Firebase Cloud Messaginggé). Az alkalmazásunk fogja meghívni azt az endpointot, a saját tokenjével, nem pedig a userekével (olyanokat is küld, de azok másfajta célokat szolgálnak). Ebben a helyzetben az alkalmazásunk lesz a kliens és a resource owner is egyben, a GCM lesz az API, ami mindezt kiszolgálja.


Viszont, ha egy Facebook/Twitter példát nézünk, akkor némileg más a helyzet. A fenti példában nem a felhasználónk kerül átirányításra a Google felé, hogy belépjen a mi felhasználónkkal és utána rányomjon az allow gombra, hanem az elején, mikor összelőjük a dolgokat, akkor nekünk kell (utána már csak refresheljük a dolgot, vagy nem jár le a token, de ez más kérdés). Figyeltétek a legutóbbi mondatot? Belép. Eddig sehol nem volt szó login formokról, márpedig azért, mert azt nekünk kell implementálni és a login form után kézzel ráhívni a handleAuthorizeRequest metódust, a plusz userId paraméterrel.

Ez volt az a pont, amikor inkább felhagytam ezzel. A terv az volt, hogy összekattintgatjuk a dolgot, pár apróságon állítunk, de nem így volt, ráadásul nem tudjuk az egyes erőforrásokat szétszeparálni és scope-okhoz rendelni, max. elég sok hegesztés árán, így a következő cikkben megnézzük, hogy a Laravel Passportja vajon mennyiben jobb, mint a fenti módszer?

Hozzászólások betöltése
2014-2017 © Letscode.hu. Minden jog fenntartva. Build verzió: 1.2.37

Mik azok a sütik?


As is common practice with almost all professional websites this site uses cookies, which are tiny files that are downloaded to your computer, to improve your experience. This page describes what information they gather, how we use it and why we sometimes need to store these cookies. We will also share how you can prevent these cookies from being stored however this may downgrade or 'break' certain elements of the sites functionality.

Áltálnos információkat nyújthat ez a Wikipedia cikk a HTTP sütikről...

Hogy használjuk a sütiket?


We use cookies for a variety of reasons detailed below. Unfortunately is most cases there are no industry standard options for disabling cookies without completely disabling the functionality and features they add to this site. It is recommended that you leave on all cookies if you are not sure whether you need them or not in case they are used to provide a service that you use.

Sütik kikapcsolása


You can prevent the setting of cookies by adjusting the settings on your browser (see your browser Help for how to do this). Be aware that disabling cookies will affect the functionality of this and many other websites that you visit. Disabling cookies will usually result in also disabling certain functionality and features of the this site. Therefore it is recommended that you do not disable cookies.

A sütik amiket mi használunk


This site offers newsletter or email subscription services and cookies may be used to remember if you are already registered and whether to show certain notifications which might only be valid to subscribed/unsubscribed users.

In order to provide you with a great experience on this site we provide the functionality to set your preferences for how this site runs when you use it. In order to remember your preferences we need to set cookies so that this information can be called whenever you interact with a page is affected by your preferences.


Harmadik féltől származó sütik


In some special cases we also use cookies provided by trusted third parties. The following section details which third party cookies you might encounter through this site.

This site uses Google Analytics which is one of the most widespread and trusted analytics solution on the web for helping us to understand how you use the site and ways that we can improve your experience. These cookies may track things such as how long you spend on the site and the pages that you visit so we can continue to produce engaging content.

For more information on Google Analytics cookies, see the official Google Analytics page.

The Google AdSense service we use to serve advertising uses a DoubleClick cookie to serve more relevant ads across the web and limit the number of times that a given ad is shown to you.

For more information on Google AdSense see the official Google AdSense privacy FAQ.

We also use social media buttons and/or plugins on this site that allow you to connect with your social network in various ways. For these to work the following social media sites including; Facebook, will set cookies through our site which may be used to enhance your profile on their site or contribute to the data they hold for various purposes outlined in their respective privacy policies.


More Information


Hopefully that has clarified things for you and as was previously mentioned if there is something that you aren't sure whether you need or not it's usually safer to leave cookies enabled in case it does interact with one of the features you use on our site. However if you are still looking for more information then you can contact us through one of our preferred contact methods.

Email: fejlesztes@letscode.hu

Bezárás