Uz već podržane protokole kao što su SAML i CAS, sustav AAI@EduHr nudi mogućnost autenticiranja korisnika pomoću protokola OpenID Connect (OIDC). OIDC je dodatni sloj na protokol OAuth2, pa se često može vidjeti referenca na taj protokol kao OAuth2 / OIDC. Prvenstvena namjena protokola OIDC je autentikacija (authn), dok je za OAuth2 to autorizacija (authz). OIDC donosi dodatne elemente u autorizacijski tijek protokola OAuth2, čime se omogućuje postupak autentikacije. Na primjer, OIDC ima predefinirane opsege (eng. scopes) za definiranje korisničkih atributa koji će se isporučivati, te ID token kao dodatni tip tokena uz pristupni token (eng. access token) iz OAuth2. ID token ima određeni format (JWT: JSON web token), dok je format pristupnog tokena nedefiniran. Upravo je dodatak ID tokena u autorizacijski tijek protokola OAuth2 dio kojim se omogućuje standardizirani postupak autentikacije korisnika.
U ovim uputama nastoji se sažeto opisati informacije potrebne za uspješno obavljanje autentikacije, no u slučaju nedoumica uvijek je dobro referirati se na originalne specifikacije protokola. Slijedi lista specifikacija koje su na neki način povezane uz postupak autentikacije pomoću protokola OIDC (redosljed ne predstavlja važnost):
AAI@EduHr ne podržava sve autentikacijske tijekove definirane u protokolu OIDC, ali podržava onaj najpopularniji i najvažniji tijek - tijek autorizacijskog koda (eng. Authorization Code Flow). Uz to, podržan je i implicitni OAuth2 tijek.
Slijedeći specifikacije, a možebitno i ove upute, implementacija autentikacije pomoću protokola OIDC se može učiniti kompliciranom. No, treba imati na umu da je protokol OIDC prilično popularan i raširen, te da već postoji mnoštvo klijenata napisanih u različitim programskim jezicima koji implementaciju svode na podešavanje konfiguracije i pozivanje nekoliko metoda čiji su krajnji rezultat informacije o autenticiranom korisniku. Da biste dobili dojam kako to može na kraju izgledati, svakako bacite pogled na primjere implementacije.
Prije nego vaša aplikacija (usluga, resurs) može početi koristiti protokol OIDC za autentikaciju korisnika u sustavu AAI@EduHr, potrebno je obaviti registraciju OIDC klijenta, a time i samog resursa, tj. vaše usluge, u AAI@EduHr Registru resursa. Pod OIDC klijentom se podrazumijeva aplikacija, dio aplikacije, modul, biblioteka ili slično, koja podnosi zathjeve za autentikacijom krajnjeg korisnika na autentikacijski poslužitelj i obrađuje odgovore iz kojih dohvaća korisničke podatke.
Registracijom resursa i OIDC kijenta definirat će se:
TODO
AAI@EduHr OIDC konfiguracijski URL je: https://login.aaiedu.hr/.well-known/openid-configuration
Na tom URL-u je dostupan JSON objekt koji sadrži sljedeća svojstva:
Primjer sadržaja AAI@EduHr konfiguracije (dio vrijednosti u svojstvu 'scopes_supported' maknut radi preglednosti):
{ "issuer": "https://login.aaiedu.hr", "authorization_endpoint": "https://login.aaiedu.hr/sso/module.php/oidc/authorize.php", "token_endpoint": "https://login.aaiedu.hr/sso/module.php/oidc/access_token.php", "userinfo_endpoint": "https://login.aaiedu.hr/sso/module.php/oidc/userinfo.php", "jwks_uri": "https://login.aaiedu.hr/sso/module.php/oidc/jwks.php", "scopes_supported": [ "openid", "profile", "email", "address", "phone", "hrEduPersonUniqueID", ..., "hrEduPersonCardNum" ], "response_types_supported": [ "code", "token" ], "subject_types_supported": [ "public" ], "id_token_signing_alg_values_supported": [ "RS256" ], "code_challenge_methods_supported": [ "plain", "S256" ] } |
Svojstvo 'jwks_uri' sadrži URL na kojem je dostupan JSON web skup ključeva (eng. JSON Web Key Set - JWKS). Ukratko, JWKS je JSON objekt koji pod svojstvom 'keys' sadrži polje ključeva, dakle jedan ili više ključeva formatu JWK (JSON Web Key - JWK). To su ključevi među kojima će se nalaziti i onaj javni ključ koji se može koristiti za provjeru potpisa u ID tokenu.
Svaki JWK sadrži nekoliko generalnih svojstva koji ga opisuju:
Svojstvo | Opis,,,,, | Napomena |
---|---|---|
kty | Tip ključa (eng. key type), npr. 'RSA' ili 'EC'. | |
kid | ID ključa (eng. key ID). Jedinstveno određuje svaki ključ u skupu ključeva. | Može se koristiti za određivanje javnog ključa za provjeru potpisa u ID tokenu (ID token u zaglavlju ima naznačen ID javnog ključa). Također, u slučaju predmemoriranja (eng. caching) javnih ključeva na resursu, omogućuje jednostavniju zamjenu ključeva (eng. key rollover). |
use | Namjena ključa. Definira dali je namjena ključa enkripcija podataka (vrijednost 'enc') ili provjera potpisa (vrijednost 'sig'). | |
alg | Oznaka algoritma koji se koristi s ključem, npr. 'RS256' (SHA256 with RSA), 'ES256' (SHA256 with ECDSA)... |
Ostala svojstva ovise o korištenom tipu i algoritmu ključa.
Primjer sadržaja JWKS-a:
{ "keys": [ { "kty": "RSA", "n": "py7oHZwX71g3azW6La-cLUi-...-DS3FyOJnyY8bZS3SQ", "e": "AQAB", "kid": "69d8c46574", "use": "sig", "alg": "RS256" } ] } |
U slučaju nemogućnosti korištenja ključeva u formatu JWK, javni ključ u formatu PEM je dostupan na poveznici: https://login.aaiedu.hr/sso/module.php/saml/idp/certs.php/idp.crt |
Trajanje kodova i tokena je kako slijedi:
OIDC opsezi (eng. scopes) se koriste tijekom autentikacijskog postupka za autorizaciju pristupa pojedinim korisničkim atributima. Svaki opseg vraća skup korisničkih atributa koje u kontekstu protokola OIDC nazivamo tvrdnjama (eng. claims). Dakle, ovisno o tome koje korisničke atribute aplikacija zahtjeva, potrebno je koristiti različite opsege (minimalan skup različitih opsega tj. potrebno je zahtjevati samo potrebne opsege). Nakon uspješne autentikacije tvrdnje će biti vraćene u ID tokenu, a također će biti dostupne na krajnjoj točki 'userinfo' (eng. 'userinfo' endopoint).
AAI@EduHr podržava korištenje standardnih OIDC opesega, uz napomenu da neće isporučiti sve tvrdnje koje su specificirane OIDC standardom. Naime, u nekim slučajevima nije moguće napraviti direktno mapiranje AAI@EduHr korisničkog atributa na pojedinu OIDC tvrdnju zbog drugačijeg formata podatka kojeg koristi AAI@EduHr od onog kojeg specificira OIDC standard. Na primjer, OIDC tvrdnja 'birthdate' je u formatu ' YYYY-MM-DD', dok je AAI@EduHr korisnički atribut 'hrEduPersonDateOfBirth' u formatu 'YYYYMMDD', i slično.
U radu sa OIDC opsezima, tvrdnjama i AAI@EduHR korisničkim atributima korisno je imati sačuvane sljedeće poveznice:
Standardni OIDC opsezi su:
Prema protokolu OIDC, opseg 'openid' mora uvijek biti prisutan u autentikacijskom tijeku. Opseg 'openid' naznačuje autorizacijskom / autentikacijskom poslužitelju da je zahtjev koji dolazi zapravo autentikacijski zahtjev (a ne autorizacijski iz protokola OAuth2), te da poslužitelj treba izdati ID token koji će sadržavati tvrdnje o korisniku. Opseg 'openid' ne definira koje tvrdnje o korisniku će se isporučiti. Za definiranje koje tvrdnje o korisniku će se isporučiti potrebno je koristiti dodatne opsege, npr. standardne OIDC opsege 'profile', 'email', ili neki poseban AAI@EduHr opseg (ili više njih).
Mapiranje standardnih OIDC tvrdnji na AAI@EduHr korisničke atribute napravljeno po uzoru na REFEDS wiki članak https://wiki.refeds.org/display/GROUPS/Mapping+SAML+attributes+to+OIDC+Claims. Dodatno, u slučaju kada neki AAI@EduHr korisnički atribut može imati više vrijednosti, a prema OIDC specifikaciji odgovarajuća OIDC tvrdnja mora imati jednu vrijednost, onda se kao vrijednost te OIDC tvrdnje uzima prva vrijednost AAI@EduHr korsničkog atributa koju vrati LDAP imenik. Na primjer, prema AAI@EduHr shemi, korisnički atribut 'mail' može imati više vrijednosti. Odgovarajuća OIDC tvrdnja je 'email', a prema specifikaciji ona može imati jednu vrijednost tipa 'string'. U tom slučaju, za OIDC tvrdnju 'email' uzet će se prva vrijednost AAI@EduHr korisničkog atributa 'mail' koju vrati LDAP imenik. Pritom je potrebno imati na umu da LDAP speficikacija ne garantira redoslijed atributa (https://www.ietf.org/rfc/rfc2251.txt, poglavlje 4.1.8. Attribute).
Slijedi popis standardnih OIDC opsega zajedno sa pripadajućim tvrdnjama i AAI@EduHr korisničkih atributa čija će vrijednost predstavljati OIDC tvrdnju (mapa OIDC tvrdnji na AAI@EduHr korisničke atribute).
OIDC tvrdnja | AAI@EduHr korisnički atribut | Napomena |
---|---|---|
name | cn | |
family_name | sn | |
given_name | givenName | |
middle_name | Nije podržano. | |
nickname | displayName | |
preferred_username | hrEduPersonUniqueID | |
profile | labeledURI | |
picture | Nije podržano. | |
website | Nije podržano. | |
gender | Nije podržano. | |
birthdate | Nije podržano. | |
zoneinfo | Nije podržano. | |
locale | Nije podržano. | |
updated_at | Nije podržano. |
OIDC tvrdnja | AAI@EduHr korisnički atribut | Napomena |
---|---|---|
email_verified | Nije podržano. |
OIDC tvrdnja | AAI@EduHr korisnički atribut | Napomena |
---|---|---|
address | Nije podržano. |
OIDC tvrdnja | AAI@EduHr korisnički atribut | Napomena |
---|---|---|
phone_number | mobile, telephoneNumber | Ako je dostupan atribut 'mobile', koristi se njegova vrijednost. Inače, ako je dostupan atribut 'telephoneNumber', koristi se njegova vrijednost. OIDC preporuča format E.164, dok AAI@EduHR koristi format E.123. Tvrdnja se ipak isporučuje, jer se radi o preporučenom formatu, a ne zahtjevanom. |
phone_number_verified | Nije podržano. |
Kako bi se omogućila isporuka svih AAI@EduHr korisničkih atributa, uz standardne definirani su i posebni AAI@EduHr opsezi i tvrdnje. Uz to, AAI@EduHr opsezi omogućuju i isporuku svih vrijednosti onih atributa koji prema AAI@EduHr shemi mogu imati višestruke vrijednosti.
AAI@EduHr OIDC opsezi su definirani tako da svaki opseg sadrži jednu tvrdnju. Naziv opsega odgovara nazivu tvrdnje koju sadrži, a naziv tvrdnje odgovara nazivu AAI@EduHr korisničkog atributa kojeg tvrdnja predstavlja. Svaka AAI@EduHr tvrdnja će biti u obliku JSON polja (eng. array), neovisno o tome dali AAI@EduHr korisnički atribut kojeg predstavlja može imati višestruke vrijednosti ili ne.
Slijedi popis AAI@EduHr opsega, tvrdnji te pripadajućih AAI@EduHr korisničkih atributa.
AAI@EduHr opseg | AAI@EduHr tvrdnja | AAI@EduHr korisnički atribut |
---|---|---|
hrEduPersonUniqueID | hrEduPersonUniqueID | hrEduPersonUniqueID |
uid | uid | uid |
cn | cn | cn |
sn | sn | sn |
givenName | givenName | givenName |
telephoneNumber | telephoneNumber | telephoneNumber |
hrEduPersonExtensionNumber | hrEduPersonExtensionNumber | hrEduPersonExtensionNumber |
mobile | mobile | mobile |
facsimileTelephoneNumber | facsimileTelephoneNumber | facsimileTelephoneNumber |
hrEduPersonUniqueNumber | hrEduPersonUniqueNumber | hrEduPersonUniqueNumber |
hrEduPersonOIB | hrEduPersonOIB | hrEduPersonOIB |
hrEduPersonDateOfBirth | hrEduPersonDateOfBirth | hrEduPersonDateOfBirth |
hrEduPersonGender | hrEduPersonGender | hrEduPersonGender |
jpegPhoto | jpegPhoto | jpegPhoto |
userCertificate | userCertificate | userCertificate |
labeledURI | labeledURI | labeledURI |
hrEduPersonProfessionalStatus | hrEduPersonProfessionalStatus | hrEduPersonProfessionalStatus |
hrEduPersonAcademicStatus | hrEduPersonAcademicStatus | hrEduPersonAcademicStatus |
hrEduPersonScienceArea | hrEduPersonScienceArea | hrEduPersonScienceArea |
hrEduPersonAffiliation | hrEduPersonAffiliation | hrEduPersonAffiliation |
hrEduPersonPrimaryAffiliation | hrEduPersonPrimaryAffiliation | hrEduPersonPrimaryAffiliation |
hrEduPersonStudentCategory | hrEduPersonStudentCategory | hrEduPersonStudentCategory |
hrEduPersonExpireDate | hrEduPersonExpireDate | hrEduPersonExpireDate |
hrEduPersonTitle | hrEduPersonTitle | hrEduPersonTitle |
hrEduPersonRole | hrEduPersonRole | hrEduPersonRole |
hrEduPersonStaffCategory | hrEduPersonStaffCategory | hrEduPersonStaffCategory |
hrEduPersonGroupMember | hrEduPersonGroupMember | hrEduPersonGroupMember |
o | o | o |
hrEduPersonHomeOrg | hrEduPersonHomeOrg | hrEduPersonHomeOrg |
ou | ou | ou |
roomNumber | roomNumber | roomNumber |
postalAddress | postalAddress | postalAddress |
l | l | l |
postalCode | postalCode | postalCode |
street | street | street |
homePostalAddress | homePostalAddress | homePostalAddress |
homeTelephoneNumber | homeTelephoneNumber | homeTelephoneNumber |
hrEduPersonCommURI | hrEduPersonCommURI | hrEduPersonCommURI |
hrEduPersonPrivacy | hrEduPersonPrivacy | hrEduPersonPrivacy |
hrEduPersonPersistentID | hrEduPersonPersistentID | hrEduPersonPersistentID |
displayName | displayName | displayName |
schacUserPresenceID | schacUserPresenceID | schacUserPresenceID |
hrEduPersonCardNum | hrEduPersonCardNum | hrEduPersonCardNum |
Ovisno o tome koji opsezi se koriste u autentikacijskom tijeku (bilo standardni bilo AAI@EduHr), u ID tokenu će biti dostupne različite tvrdnje o korisniku koji se autenticirao. Uz to, ID token će sadržavati i sljedeće, stalne tvrdnje:
Tvrdnja | Opis | Napomena |
---|---|---|
iss | Identifikator izdavatelja (eng. issuer). Sadrži vrijednost AAI@EduHr identifikatora izdavatelja koji je inače dostupan na OIDC konfiguracijskom URL-u. | |
sub | Identifikator subjekta (eng. subject identifier). Sadrži jedinstveni identifikator krajnjeg korisnika tj. korisnika koji se autenticirao. | Sadrži vrijednost AAI@EduHr korisničkog atributa 'hrEduPersonPersistentID'. |
aud | Publika (eng. audience) kojoj je ID token namijenjen. Sadrži ID klijenta kojem je ID token namijenjen. | Sadrži jednu vrijednost tipa 'string'. OIDC specifikacija inače predviđa mogućnost vraćanja više vrijednosti u polju (više ID klijenata) ili jednostruku vrijednost (jedan ID klijenta). |
jti | JWT ID, jedinstveni identifikator samog ID tokena. Može se koristiti za sprječavanje ponovnog korištenja već iskorištenog ID tokena. | |
iat | Tvrdnja 'izdan u' (eng. issued at). Sadrži vremensku oznaku** kada je ID token izdan. | |
exp | Vrijeme isteka (eng. expiration time). Sadrži vremensku oznaku** nakon koje ID token ne smije biti prihvaćen. | |
nbf | Tvrdnja 'ne prije' (eng. not before). Sadrži vremensku oznaku** (eng. timestamp) prije koje ID token ne smije biti prihvaćen. | |
nonce | Vrijednost tipa 'string' koja se koristi za povezivanje sjednice (eng. session) klijenta sa ID tokenom, u svrhu sprječavanja napada ponovne reprodukcije (eng. replay attack). Vrijednost parametra 'nonce' kojeg klijent koristi tijekom autentikacije krajnjeg korisnika će biti proslijeđena u ID token. Nakon što klijent dobije ID token, mora provjeriti dali je vrijednost 'nonce' u ID tokenu ista kao i ona korištena tijekom autentikacije. |
** vrijednost tvrdnji 'iat', 'exp' i 'nbf' je broj sekundi proteklih (ili koje će proteći) od 1. siječnja 1970. godine u 0 sati mjereno po standardu UTC (1970-01-01T0:0:0Z), do predmetne vremenske oznake.
ID token je primarni način isporuke korisničkh podataka od autentikacijskog poslužitelja do resursa. ID token sadrži tvrdnje o korisniku (korisničke atribute), te druge tvrdnje o samom autentikacijskom događaju, u formatu JWT (JSON Web Token).
Ukratko, ID token se sastoji od tri dijela:
Zaglavlje i korisni podaci su Base64 URL kodirani JSON objekti (svaki zasebno), a treći dio je Base64 URL kodirani potpis generiran nad kodiranim zaglavljem i korisnim podacima koristeći privatni ključ i algoritam naznačen u zaglavlju. Svaki dio tokena je odvojen točkom, pa je krajnji oblik ID tokena niz znakova u formatu:
zzzzz.kkkkk.ppppp
Dio 'zzzzz' predstavlja zaglavlje, dio 'kkkkk' predstavlja korisne podatke, a dio 'ppppp' predstavlja potpis. Da bi se došlo do podataka, ID token se može rastaviti na tri dijela preko točke '.', pa Base64 URL dekodirati zaglavlje i korisne podatke. Potpis je potrebno iskoristiti kako bi se provjerilo da ID token tijekom puta nije promijenjen, te da je izdan od očekivanog izdavatelja.
Primjer potpunog ID tokena:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY5ZDhjNDY1NzQifQ.eyJpc3MiOiJodHRwczovL2xvZ2luLmFhaWVkdS5ociIsImF1ZCI6IjZlNTUyOTUyMDk3ODJiN2IyIiwianRpIjoiYzQ0ZjRjZmZjYzg0Zjc5OTBmN2ExZDViMmMiLCJuYmYiOjE2MDI2NzQ0NzAsImV4cCI6MTYwMjY3NTA3MCwic3ViIjoiYmZhMTYwNWJlNDRhNTBhN2MiLCJpYXQiOjE2MDI2NzQ0NzAsIm5vbmNlIjoiZHRubWVCTDVIVm5oUWtJUiIsIm5hbWUiOiJJdmFuIEhvcnZhdCIsImZhbWlseV9uYW1lIjoiSG9ydmF0IiwiZ2l2ZW5fbmFtZSI6Ikl2YW4iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJpaG9ydmF0QHByaW1qZXIuaHIiLCJlbWFpbCI6Iml2YW4uaG9ydmF0QHByaW1qZXIuaHIiLCJockVkdVBlcnNvblVuaXF1ZU51bWJlciI6WyJMT0NBTF9OTzogMTIzNCIsIk9JQjogMTIzNDU2Nzg5MTIiLCJKTUJBRzogMTIzNDU2Nzg5MSJdfQ.c5q4W3bmcxQepAHj9n7xxj8WaH0wqIaiFkOFB9UE3joeVUyU9hWuNsDfPJ_BZ7WfYnhyMrv29Ys-dHs1oKqagvqFdk157IEwLnW1Dd5vNXvgGhMBhCTtseeQBDFS_DHN6KaksFDnGtFyWh3GXq-dWEBO86fpgSB3OuV9CD-AQAAbjXsN4Mz9MyaajMkhxWgxh_7HTZMlg5hACv3Xn-wiI8N3IGDqWrgdB87Vo6n0T5TfVaUZQ8mw5ca5fKy-aH5BP940LBb3bt6CvhZqxK0XFmD_M8hh-2MyRuPf2u6_P9HUORo1f8x8ZI30J2arhghPRcqEK9Sv_hdPxMy7WJJupg |
Zaglavlje ID tokena sadrži sljedeće tvrdnje.
Tvrdnja | Opis | Napomena |
---|---|---|
typ | Tip tokena. | Vrijednost je 'JWT'. |
alg | Algoritam korišten za potpisivanje tokena. | Vrijednost je jedna od vrijednosti JSON svojstva ' id_token_signing_alg_values_supported ' na OIDC konfiguracijskom URL-u. |
kid | ID ključa (eng. key ID) koji je korišten za potpisivanje tokena. | Vrijednost je jednaka vrijednosti svojstva 'kid' od jednog javnog ključa dostupnom u JSON web skupu ključeva (JSON web skup ključeva dostupan je na URL-u navedenom u JSON svojstvu 'jwks_uri' na OIDC konfiguracijskom URL-u). |
Primjer Base64 URL dekodiranog zaglavlja ID tokena:
{ "typ": "JWT", "alg": "RS256", "kid": "69d8c46574" } |
Drugi dio ID tokena sadrži korisne podatke, tj. tvrdnje o krajnjem korisniku (korisniku koji se autenticirao), te o autentikacijskom događaju. Uz stalne tvrdnje ('iss', 'sub', ...), ovisno o tome koji se sve opsezi koriste, ovaj JSON objekt će sadržavati različite podatke.
Primjer Base64 URL dekodiranih korisnih podataka:
{ "iss": "https://login.aaiedu.hr", "aud": "6e55295209782b7b2", "jti": "c44f4cffcc84f7990f7a1d5b2c", "nbf": 1602674470, "exp": 1602675070, "sub": "bfa1605be44a50a7c", "iat": 1602674470, "nonce": "dtnmeBL5HVnhQkIR", "name": "Ivan Horvat", "family_name": "Horvat", "given_name": "Ivan", "preferred_username": "ihorvat@primjer.hr", "email": "ivan.horvat@primjer.hr", "hrEduPersonUniqueNumber": [ "LOCAL_NO: 1234", "OIB: 12345678912", "JMBAG: 1234567891" ] } |
Potpis je treći (zadnji) dio ID tokena i generiran je nad Base64 URL kodiranim zaglavljem i Base 64 URL kodiranim korisnim podacima, povezano točkom '.' (nad prvim i drugim dijelom ID tokena); dakle nad nizom znakova 'zzzzz.kkkkk', koristeći algoritam naznačen u zaglavlju ID tokena.
AAI@EduHr za generiranje potpisa koristi algoritam 'RS256'. U postupku kreiranja potpisa koristi se privatni ključ iz para javnog i privatnog RSA ključa. Za provjeru tog potpisa potrebno je iskoristiti javni ključ koji je dostupan u JSON web skupu ključeva. Odgovarajući javni ključ se može pronaći preko ID ključa ('kid') koji je naznačen u zaglavlju ID tokena te u pojedinom ključu u JSON web skupu ključeva. |
Primjer Base64 URL kodiranog potpisa:
c5q4W3bmcxQepAHj9n7xxj8WaH0wqIaiFkOFB9UE3joeVUyU9hWuNsDfPJ_BZ7WfYnhyMrv29Ys-dHs1oKqagvqFdk157IEwLnW1Dd5vNXvgGhMBhCTtseeQBDFS_DHN6KaksFDnGtFyWh3GXq-dWEBO86fpgSB3OuV9CD-AQAAbjXsN4Mz9MyaajMkhxWgxh_7HTZMlg5hACv3Xn-wiI8N3IGDqWrgdB87Vo6n0T5TfVaUZQ8mw5ca5fKy-aH5BP940LBb3bt6CvhZqxK0XFmD_M8hh-2MyRuPf2u6_P9HUORo1f8x8ZI30J2arhghPRcqEK9Sv_hdPxMy7WJJupg |
Podržani autentikacijski tijekovi (eng. flows) su:
Uz to, podržan je i tijek osvježavanja tokena (eng. refresh token flow) koji omogućuje ponovno izdavanje pristupnog tokena i ID tokena bez ponovne autentikacije krajnjeg korisnika.
Svaki autentikacijski tijek se obavlja pomoću niza HTTP zahtjeva i preusmjeravanja (eng. redirections) između aplikacije (resursa) i autentikacijskog poslužitelja. Različiti autentikacijski tijekovi znače različit način slanja HTTP zahtjeva i različit način dohvata korisničkih podataka nakon autentikacije. Također, različiti tijekovi donose i različite razine sigurnosti autentikacijskog postupka. Na primjer, od trenutno podržana dva tijeka, tijek autorizacijskog koda ima veću razinu sigurnosti od implicitnog tijeka.
AAI@EduHr preporuča korištenje tijeka autorizacijskog koda za autentikaciju korisnika, neovisno tome koji tip klijenta se koristi ili u kojem programskom jeziku je klijent implementiran. |
Krajnje točke na autentikacijskom poslužitelju koje se mogu koristiti su:
Ovisno o tome koji autentikacijski tijek se korsti, koristit će se različite krajnje točke i različiti HTTP zahtjevi. Autentikacijski tijek se određuje tako da se u početnom HTTP zahtjevu na autorizacijsku krajnju točku postavi parametar 'response_type' s jednom od vrijednosti:
Autorizacijska i token krajnja točka se koriste u postupku autentikacije tj. za izdavanje ID tokena i / ili pristupnog tokena. Krajnja točka za korisničke informacije se koristi nakon što je autentikacija obavljena, a služi za dohvat korisničkih podataka pomoću pristupnog tokena u slučaju kada se u autentikacijskom tijeku ne izdaje ID token. Dakle, krajnja točka za korisničke informacije se koristi kad se u tijeku autorizacijskog koda ne koristi opseg 'openid' (dakle u slučaju kada se ne izdaje ID token, nego samo pristupni token) ili u OAuth2 implicitnom tijeku (jer se izdaje samo pristupni token). Slijedi više informacija o svakom autentikacijskom tijeku.
Podržane vrijednosti parametra 'response_type' su navedene u svojstvu 'response_types_supported' u JSON objektu na OIDC konfiguracijskom URL-u. Time se definiraju autentikacijski tijekovi koje podržava AAI@EduHr. Inače, protokol OIDC definira i sljedeće vrijednosti parametra 'response_type' tj. dodatne autentikacijske tijekove koje AAI@EduHr trenutno ne podržava:
|
Tijek autorizacijskog koda je glavni i preporučenin način autenticiranja krajnjih korisnika koristeći protokol OIDC. Tijek autorizacijskog koda u protokolu OIDC je nadogradnja već postojećeg tijeka autorijacijskog koda u protokolu OAuth2. Glavne promjene koje u ovaj tijek donosi protokol OIDC u odnosu na OAuth2 su preddefinirani OIDC opsezi i mogućnost izdavanja ID tokena.
Detaljni opis tijeka dostupan je u specifikacijama:
Ukratko, tijek autorizacijskog koda se obavlja na način:
Dakle, klijent dobije autorizacijski kod kojeg onda u pozadinskom kanalu (eng. back channel) direktno zamjeni za pristupni token i ID token. To znači da pristupni token i ID token neće biti izložen u tkz. prednjem kanalu (eng. front channel), dakle korisničkom agentu (eng. user-agent) kao što je web preglednik (a time i svim možebitnim malicioznim aplikacijama koje imaju pristup korisničkom agentu). Također, autorizacijski server može autenticirati klijenta prije izdavanja tokena.
Slijedi opis svih HTTP zahtjeva.
Prema protokolu OIDC, ovaj HTTP zahtjev može biti tipa GET ili POST. AAI@EduHr trenutno podržava samo tip GET. |
Autentikacijski tijek počinje HTTP zahtjevom na autorizacijsku krajnju točku (eng. authorization endpoint). URL autorizacijske krajnje točke je dostupan u svojstvu 'authorization_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u. Ovaj HTTP zahtjev se obavlja preusmjeravanjem korisničkog agenta (tipično web preglednika) na autentikacijski poslužitelj preko unaprijed pripremljenog URL-a s točno definiranim GET parametrima. Primjer pripremljenog URL-a za preusmjeravanje korisničkog agenta:
https://login.aaiedu.hr/sso/module.php/oidc/authorize.php?response_type=code&client_id=neki-id-klijenta&redirect_uri=https://neki-uri-za-preusmjeravanje.primjer.hr&scope=openid profile&state=neki-state-132&nonce=neki-nonce-123 |
Ovisno o tome koji tip klijenta se koristi, URL mora sadržavati određene GET parametre. Slijedi opis obaveznih i opcionalnih GET parametra:
Parametar | Vrijednost | Napomena |
---|---|---|
response_type | 'code' | Obavezan parametar. Vrijednost 'code' signalizira da se radi o tijeku autorizacijskog koda. |
client_id | ID klijenta koji se dobije prilikom registracije klijenta. | Obavezan parametar. |
redirect_uri | Jedna od vrijednosti lokacija za preusmjeravanje koje su definirane prilikom registracije klijenta. | Obavezan parametar. |
scope | Lista standardnih ili AAI@EduHr opsega odvojenih razmakom. Moguće vrijednosti opsega su navedene u JSON svojstvu 'scopes_supported' na OIDC konfiguracijskom URL-u. | Obavezan parametar. Za obavljanje autentikacije po protokolu OIDC, jedan od opsega mora biti 'openid'. Ako opseg 'openid' nije naveden, obavit će se tijek autorizacijskog koda po protokolu OAuth2. |
code_challenge_method | Jedna od sljedećih vrijednosti: 'plain', 'S256'. | Obavezan parametar za javne (eng. public) klijente. Kada je klijent sposoban koristiti metodu 'S256', mora ju koristiti umjesto metode 'plain'. |
code_challenge | Ako parametar 'code_challenge_method' koristi vrijednost 'plain', onda je vrijednost parametra 'code_challenge' jednaka vrijednosti parametra 'code_verifier'. Ako parametar 'code_challenge_method' koristi vrijednost 'S256', onda je vrijednost parametra 'code_challenge' jednaka vrijednosti koja se dobije SHA256 raspršenjem (eng. hash) vrijednosti parametra 'code_verifier'. | Obavezan parametar za javne (eng. public) klijente. Za generiranje vrijednosti parametra 'code_challenge' koristi se parametar 'code_verifier', koji mora biti unaprijed pripreljmen. Parametar 'code_verifier' se ne koristi u ovom zahtjevu, nego u idućem zahtjevu na token krajnju točku. |
state | Ako se koristi, vrijednost mora biti niz znakova sa dovoljnom entropijom, dakle kriptografski siguran (nesmije ga biti lako pogoditi). Preporuča se napraviti npr. raspršivanje (eng. hash) kolačića sjednice (eng. session cookie), ili raspršivanje kriptografski slučajnog niza znakova (eng. random string) po svakom zahtjevu. | Opcionalan parametar, ali je preporučen zbog zaštite od napada krivotvorenja zahtjeva za više web lokacija (eng. Cross-Site Request Forgery - CSRF). Vrijednost parametra koja je dana u ovom zahtjevu će biti vraćena kao jedan od parametara kod preusmjeravanja na registriranu lokaciju za preusmjeravanje, pa ju je tamo potrebno i provjeriti (mora biti ista). Na taj način se osigurava da na lokaciji za preumjeravanje stignu HTTP zahtjevi koje je klijent tražio (dakle, samo odgovori na autorizacijski zahtjev). Parametar 'state' se ponekad koristi i za postavljanje URL-a na kojeg je kranjeg korisnika potrebno preusmjeriti nakon procesa autentikacije (implementirati po potrebi, uz napomenu da bi URL za preusmjeravanje također trebao sadržavati slučajan niz znakova, npr. kao GET parametar, kako bi se očuvao smisao korištenja parametra). |
nonce | Ako se koristi, vrijednost mora biti niz znakova sa dovoljnom entropijom, dakle kriptografski siguran (nesmije ga biti lako pogoditi). Preporuča se napraviti npr. raspršivanje (eng. hash) kolačića sjednice (eng. session cookie), ili raspršivanje kriptografski slučajnog niza znakova (eng. random string) po svakom zahtjevu. | Opcionalan parametar, ali je preporučen zbog zaštite od napada ponovne reprodukcije (eng. replay attack). Vrijednost parametra koja je dana u ovom zahtjevu će biti vraćena kao jedna od tvrdnji u ID tokenu, pa ju je tamo potrebno i provjeriti (mora biti ista). Na taj način se povezuje autorizacijski zahtjev sa izdanim ID tokenom, pa klijent može potvrditi da je to ID token koji je zatražen. |
U nastavku je opis HTTP GET zahtjeva kojeg korisnički agent obavi nakon što ga se preusmjeri na pripremljeni URL:
GET {Authorization Endpoint} ?response_type=code // Obavezno &client_id={Client ID} // Obavezno &redirect_uri={Redirect URI} // Obavezno &scope={Scopes} // Obavezno &state={Arbitrary String} // Preporučeno zbog zaštite od napada krivotvorenja zahtjeva za više web lokacija (eng. Cross-Site Request Forgery - CSRF) &code_challenge={Challenge} // Obavezno za javne (eng. public) klijente, za povjerljive (engl. confidential) klijente se ne koristi &code_challenge_method={Method} // Obavezno za javne (eng. public) klijente, za povjerljive (engl. confidential) klijente se ne koristi &nonce={Nonce} // Preporučeno zbog zaštite od napada ponovne reprodukcije (eng. replay attack) HTTP/1.1 HOST: {Authorization Server} |
Parametri 'code_verifier', 'code_challenge' i 'code_challenge_method' su dio dodatnog standarda 'Proof Key for Code Exchange by OAuth Public Clients - PKCE' kojim se želi poboljšati sigurnost za javne klijente. Upravo zbog tog standarda, javnim klijentima se omogućuje i preporučuje korištenje tijeka autorizacijskog koda za autentikaciju, umjesto da se koristi implicitni tijek. Vrijednosti koje mogu poprimiti parametri 'code_verifier', 'code_challenge' i 'code_challenge_method' detaljno su opisan su u specifikaciji PKCE u poglavlju https://tools.ietf.org/html/rfc7636#section-4. |
Nakon što se krajnji korisnik autenticira, autentikacijski poslužitelj će generirati kratkotrajni autorizacijski kod i vratit ga klijentu preusmjeravanjem na lokaciju za preusmjeravanje koja je korištena u prvom zahtjevu na autorizacijsku krajnju točku. Također, ako je korišten parametar 'state', vratit će i njega. Vrijednost parametra 'state' je potrebno provjeriti na klijentskoj strani (mora biti isti onaj koji je korišten u prvom zahtjevu prema autorizacijskoj krajnjoj točki).
HTTP/1.1 302 Found Location: {Redirect URI} ?code={Authorization Code} // Uvijek uključen &state={Arbitrary String} // Uključen ako je zahtjev na autorizacijsku krajnju točku imao parametar 'state' |
Ovaj zahtjev odrađuje se u pozadinskom kanalu (eng. back channel) na način da klijent samostalno šalje HTTP POST zahtjev na token krajnju točku (web preglednik nema uvid u ovaj zahtjev). URL token krajnje točke je dostupan u svojstvu 'token_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u.
POST {Token Endpoint} HTTP/1.1 Host: {Authorization Server} Content-Type: application/x-www-form-urlencoded grant_type=authorization_code // Obavezno &client_id={Client ID} // Obavezno &client_secret={Client secret} // Obavezno za povjerljive (eng. confidential) klijente. Za javne (eng. public) klijente se ne postavlja. &code={Authorization Code} // Obavezno &redirect_uri={Redirect URI} // Obavezno &code_verifier={Verifier} // Obavezno za javne (eng. public) klijente. Za povjerljive (eng. confidential) klijente se ne postavlja. |
Opis GET parametara:
Parametar | Vrijednost | Napomena |
---|---|---|
grant_type | 'authorization_code' | Vrijednost 'authorization_code' signalizira da se koristi autorizacijski kod u svrhu izdavanja tokena. |
client_id | ID klijenta koji se dobije prilikom registracije klijenta. | |
client_secret | Tajni ključ klijenta koji se definira prilikom registracije klijenta. | Koristi se samo za povjerljive klijente (ne postavljati ako je klijent javan). |
code | Autorizacijski kod dobiven od autentikacijskog poslužitelja. | |
redirect_uri | Jedna od vrijednosti lokacija za preusmjeravanje koje su definirane prilikom registracije klijenta. | |
code_verifier | Ako se koristi, vrijednost mora biti Base64 URL kodiran niz znakova minimalne duljine 43 i maksimalne duljine 128. Niz znakova mora biti sa dovoljnom entropijom, dakle kriptografski siguran (nesmije ga biti lako pogoditi). | Obavezan za javne klijente. Parametar 'code_verifier' mora biti unaprijed definiran i pohranjen jer se koristi i za generiranje parametra 'code_challenge' koji se šalje u prethodnom, autorizacijskom zahtjevu na autentikacijski poslužitelj. |
U slučaju da je čitav tijek prošao u redu, ovaj HTTP odgovor sadrži krajnji rezultat autentikacije, tokene. Odgovor će uvijek sadržavati pristupni token (eng. access token). Ako je u atorizacijskom zahtjevu korišten opseg 'openid', sadržavat će i ID token. Uz njih, bit će izdan i token za osvježavanje (eng. refresh token).
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "id_token": "{ID Token}", // Uključen ako je scope sadržavao vrijednost 'openid', inače ne "access_token": "{Access Token}", // Uključen "token_type": "{Token Type}", // Uključen "expires_in": {Lifetime In Seconds}, // Uključen "refresh_token": "{Refresh Token}", // Uključen } |
Ako je vraćen ID token, potrebno ga je provjeriti na način:
Ako bilo koja provjera ne uspije, potrebno je odbaciti ID token i prekinuti autentikacijski tijek.
U slučaju da se u tijeku autorizacijskog koda ne korsiti opseg 'openid', dakle u slučaju kada se ne koristi mogućnost izdavanja ID tokena (što ne preporučamo), korisnički podaci se mogu dohvatiti slanjem dodatnog HTTP zahtjeva na krajnju točku za korisničke podatke (eng. userinfo endopoint). URL krajnje točke za korisničke podatke je dostupan u svojstvu 'userinfo_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u. U HTTP zahtjevu je potrebno postaviti 'Authorization' HTTP zaglavlje koje će za vrijednost imati 'Bearer {Access Token}' (umjesto '{Access Token}' iskoristiti pristupni token dobiven u prethodnom zahtjevu prema token krajnjoj točki).
GET {Userinfo Endpoint} HTTP/1.1 Authorization: Bearer {Access Token} Host: {Authorization Server} |
Primjer odgovora sa krajnje točke za korisničke podatke:
HTTP/1.1 200 OK Content-Length: 25 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: application/json {"sub":"bfa1605be44a50a7c","name":"Ivan Horvat","family_name":"Horvat","given_name":"Ivan","preferred_username":"ihorvat@primjer.hr","email":"ivan.horvat@primjer.hr","hrEduPersonUniqueNumber":["LOCAL_NO: 1234","OIB: 12345678912","JMBAG: 1234567891"]} |
Ovo je standardni OAuth2 implicitni tijek, što znači da se izdaje samo pristupni token (eng. access token). ID token se ne izdaje, neovisno o tome dali je u parametru 'scope' navedena vrijednost 'openid'. Ovaj tijek koristi samo autorizacijsku krajnju točku koja izdaje pristupni token (uopće se ne koristi token krajnja točka). Za dohvat podataka o korisniku koristi se krajnja točka za korisničke podatke (eng. userinfo krajnja točka). Ovaj tijek ne izdaje token za osvježavanje (eng. refresh token).
Zbog veće razine sigurnosti, AAI@EduHr preporuča korištenje tijeka autorizacijskog koda umjesto implicitnog tijeka. |
Detaljan opis tijeka dostupan je u specifikaciji OAuth2: https://tools.ietf.org/html/rfc6749#section-4.2 .
Ukratko, implicitni tijek se obavlja na način:
Slijedi opis pojedinih HTTP zahtjeva.
Autentikacijski tijek počinje HTTP zahtjevom na autorizacijsku krajnju točku (eng. authorization endpoint). URL autorizacijske krajnje točke je dostupan u svojstvu 'authorization_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u. Ovaj HTTP zahtjev se obavlja preusmjeravanjem korisničkog agenta (tipično web preglednika) na autentikacijski poslužitelj preko unaprijed pripremljenog URL-a s točno definiranim GET parametrima. Primjer pripremljenog URL-a za preusmjeravanje korisničkog agenta:
https://login.aaiedu.hr/sso/module.php/oidc/authorize.php?response_type=code&client_id=neki-id-klijenta&redirect_uri=https://neki-uri-za-preusmjeravanje.primjer.hr&scope=profile email&state=neki-state-132 |
Ovisno o tome koji tip klijenta se koristi, URL mora sadržavati određene GET parametre. Slijedi opis obaveznih i opcionalnih GET parametra:
Parametar | Vrijednost | Napomena |
---|---|---|
response_type | 'token' | Obavezan parametar. Vrijednost 'token' signalizira da se radi o implicitnom tijeku. |
client_id | ID klijenta koji se dobije prilikom registracije klijenta. | Obavezan parametar. |
redirect_uri | Jedna od vrijednosti lokacija za preusmjeravanje koje su definirane prilikom registracije klijenta. | Obavezan parametar. |
scope | Lista standardnih ili AAI@EduHr opsega odvojenih razmakom. Moguće vrijednosti opsega su navedene u JSON svojstvu 'scopes_supported' na OIDC konfiguracijskom URL-u. | Obavezan parametar. |
state | Ako se koristi, vrijednost mora biti niz znakova sa dovoljnom entropijom, dakle kriptografski siguran (nesmije ga biti lako pogoditi). Preporuča se napraviti npr. raspršivanje (eng. hash) kolačića sjednice (eng. session cookie), ili raspršivanje kriptografski slučajnog niza znakova (eng. random string) po svakom zahtjevu. | Opcionalan parametar, ali je preporučen zbog zaštite od napada krivotvorenja zahtjeva za više web lokacija (eng. Cross-Site Request Forgery - CSRF). Vrijednost parametra koja je dana u ovom zahtjevu će biti vraćena kao jedan od parametara kod preusmjeravanja na registriranu lokaciju za preusmjeravanje, pa ju je tamo potrebno i provjeriti (mora biti ista). Na taj način se osigurava da na lokaciji za preumjeravanje stignu HTTP zahtjevi koje je klijent tražio (dakle, samo odgovori na autorizacijski zahtjev). Parametar 'state' se ponekad koristi i za postavljanje URL-a na kojeg je kranjeg korisnika potrebno preusmjeriti nakon procesa autentikacije (implementirati po potrebi, uz napomenu da bi URL za preusmjeravanje također trebao sadržavati slučajan niz znakova, npr. kao GET parametar, kako bi se očuvao smisao korištenja parametra). |
U nastavku je opis HTTP GET zahtjeva kojeg korisnički agent obavi nakon što ga se preusmjeri na pripremljeni URL:
GET {Authorization Endpoint} ?response_type=token // Obavezno &client_id={Client ID} // Obavezno &redirect_uri={Redirect URI} // Obavezno &scope={Scopes} // Obavezno &state={Arbitrary String} // Preporučeno zbog zaštite od napada krivotvorenja zahtjeva za više web lokacija (eng. Cross-Site Request Forgery - CSRF) HTTP/1.1 HOST: {Authorization Server} |
Nakon što se krajnji korisnik autenticira, autentikacijski poslužitelj će generirati pristupni token i vratit ga klijentu preusmjeravanjem na lokaciju za preusmjeravanje koja je korištena u prvom zahtjevu na autorizacijsku krajnju točku. Također, ako je korišten parametar 'state', vratit će i njega. Svi parametri će biti vraćeni u fragment dijelu URL-a (nakon znaka #). Vrijednost parametra 'state' je potrebno provjeriti na klijentskoj strani (mora biti isti onaj koji je korišten u prvom zahtjevu prema autorizacijskoj krajnjoj točki).
HTTP/1.1 302 Found Location: {Redirect URI} #access_token={Access Token} // Uključen &token_type={Token Type} // Uključen &expires_in={Lifetime In Seconds} // Uključen &state={Arbitrary String} // Uključen ako je zahtjev na autorizacijsku krajnju točku imao parametar 'state' |
Korisnički podaci se dohvaćaju slanjem HTTP zahtjeva na krajnju točku za korisničke podatke (eng. userinfo endopoint). URL krajnje točke za korisničke podatke je dostupan u svojstvu 'userinfo_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u. U HTTP zahtjevu je potrebno postaviti 'Authorization' HTTP zaglavlje koje će za vrijednost imati 'Bearer {Access Token}' (umjesto '{Access Token}' iskoristiti pristupni token dobiven u prethodnom zahtjevu prema autorizacijskoj krajnjoj točki).
GET {Userinfo Endpoint} HTTP/1.1 Authorization: Bearer {Access Token} Host: {Authorization Server} |
Primjer odgovora sa krajnje točke za korisničke podatke:
HTTP/1.1 200 OK Content-Length: 25 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: application/json {"sub":"bfa1605be44a50a7c","name":"Ivan Horvat","family_name":"Horvat","given_name":"Ivan","preferred_username":"ihorvat@primjer.hr","email":"ivan.horvat@primjer.hr","hrEduPersonUniqueNumber":["LOCAL_NO: 1234","OIB: 12345678912","JMBAG: 1234567891"]} |
Tijek tokena za osvježavanje (eng. refresh token flow) omoućuje ponovni dohvat pristupnog i ID tokena, bez potrebe ponovne autentikacije krajnjeg korisnika. Za osvježavanje tokena koristi se token krajnja točka čiji je URL dostupan u svojstvu 'token_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u.
Slijedi opis pojedinih HTTP zahtjeva.
Opis potrebnih POST parametara:
Parametar | Vrijednost | Napomena |
---|---|---|
grant_type | 'refresh_token' | Obavezan parametar. Vrijednost 'refresh_token' signalizira da se radi o tijeku tokena za osvježavanje. |
refresh_token | Vrijednost tokena za osvježavanje dobivena u tijeku autorizacijskog koda. | Obavezan parametar. |
client_id | ID klijenta koji se dobije prilikom registracije klijenta. | Obavezan parametar. |
client_secret | Tajni ključ klijenta koji se definira prilikom registracije klijenta. | Obavezan parametar. |
scope | Ako se koristi, vrijednost mora biti ili dio originalno odabranih opsega ili svi originalni opsezi. Ne mogu se dodavati novi opsezi koji nisu bili u originalnom autorizacijskom zahtjevu. | Opcionalan parametar. Ako se ne koristi, podrazumijevana vrijednost je originalno korištena lista opsega u autorizacijskom zahtjevu. |
Primjer HTTP POST zahtjeva:
POST {Token Endpoint} HTTP/1.1 Host: {Authorization Server} Content-Type: application/x-www-form-urlecoded grant_type=refresh_token // Obavezno &refresh_token={Refresh Token} // Obavezno &client_id={Client ID} // Obavezno &client_secret={Client secret} // Obavezno za 'confidential' klijente. Za 'public' klijente se ne postavlja &scope={Scopes} // Opcionalno |
Odgovor će uvijek sadržavati novi pristupni token i token za osvježavanje. Ako je u originalnom zahtjevu korišten opseg 'openid', odgovor će sadržavati i osvježeni ID token. ID token će imati tvrdnje 'iat' i 'nbf' postavljene na vremensku oznaku (eng. timestamp) u trenutku osvježavanja. Ostale tvrdnje će imati vrijednosti kao i u originalnom ID tokenu.
Primjer odgovora:
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token": "{Access Token}", // Uključen "token_type": "{Token Type}", // Uključen "id_token": "{ID Token}", // Uključen ako je originalni autorizacijski zahtjev u parametru scope sadržavao vrijednost 'openid', inače ne "expires_in": {Lifetime In Seconds}, // Uključen "refresh_token": "{Refresh Token}", // Uključen } |
Slijedi nekoliko primjera implementacije autentikacije u nekim programskim jezicima koristeći protokol OIDC. Navedeni primjeri su dani u pokazne svrhe te ne predstavljaju obavezu korištenja spomenutih programskih paketa, biblioteka, razvojnih okvira ili slično. No, svakako je preporuka koristiti neki postojeći, popularniji OIDC programski paket, jer oni tipično omogućuju laganiju i sigurniju implementaciju autentikacije. Tijekom stvaranja primjera korištene su tada aktualne verzije paketa, pa je moguće da se način implementacije pomoću njih u međuvremenu promijenio. Za najnovije upute potrebno je proučiti dokumentaciju samog programskog paketa koji se koristi.
Radi jednostavnosti prikaza samog koda, u primjerima su napravljene neke stvari koje bi u stvarnim aplikacijama trebalo izbjegavati:
Svi primjeri PHP paketa se mogu instalirati pomoću alata Composer.
Repozitorij paketa: https://github.com/jumbojett/OpenID-Connect-PHP
U primjeru, čitav kod je sadržan u jednoj datoteci 'index.php'. U njoj se započinje postupak autentikacije, te također služi i kao lokacija za preusmjeravanje na koju će autentikacijski server vratiti autorizacijski kod. Paket ima mogućnost automatskog dohvata OIDC konfiguracije sa OIDC konfiguracijskog URL-a (nije potrebno zasebno definirati krajnje točke). U trenutku pisanja primjera, ovaj paket je mogao koristiti samo certifikat u PEM formatu (ne koristi funkcionalnost dohvata javnog ključa iz JSON web skupa ključeva).
<?php require 'vendor/autoload.php'; if (session_status() == PHP_SESSION_NONE) { session_start(); } // TODO In real app, check if the user is already authenticated locally. // If so, prevent further processing, show warning message, redirect to another page, or similar. // Do the OIDC authentication. $issuer = 'https://login.aaiedu.hr'; $clientId = 'YOUR_CLIENT_ID'; $clientSecret = 'YOUR_CLIENT_SECRET'; $oidcClient = new \Jumbojett\OpenIDConnectClient($issuer, $clientId, $clientSecret); // Provide the path to the public certificate from OpenID Provider (OP) $oidcClient->setCertPath(__DIR__ . '/../storage/idp.crt'); $oidcClient->addScope(['openid', 'profile', 'hrEduPersonUniqueNumber']); $oidcClient->setRedirectURL('https://your-app.example.org/index.php'); // Start authentication or handle incoming authorization code. $oidcClient->authenticate(); // Use appropriate methods to get user data. // TODO In real app, log in the user locally, show success message, or similar. var_dump($oidcClient->requestUserInfo('sub')); var_dump($oidcClient->getVerifiedClaims()); |
Repozitorij paketa: https://github.com/steverhoades/oauth2-openid-connect-client
U primjeru postoje tri datoteke:
U trenutku pisanja primjera, ovaj paket je mogao koristiti samo certifikat u PEM formatu (ne koristi funkcionalnost dohvata javnog ključa iz JSON web skupa ključeva). Potrebno je ručno unijeti URL-ove za krajnje točke (nema mogućnost automatskog dohvata OIDC konfiguracije sa OIDC konfiguracijskog URL-a).
<?php require 'vendor/autoload.php'; if (session_status() == PHP_SESSION_NONE) { session_start(); } $oidcClient = new \OpenIDConnectClient\OpenIDConnectProvider( [ 'clientId' => 'YOUR_CLIENT_ID', 'clientSecret' => 'YOUR_CLIENT_SECRET', 'idTokenIssuer' => 'https://login.aaiedu.hr', 'redirectUri' => 'https://your-app.example.org/callback.php', 'urlAuthorize' => 'https://login.aaiedu.hr/sso/module.php/oidc/authorize.php', 'urlAccessToken' => 'https://login.aaiedu.hr/sso/module.php/oidc/access_token.php', 'urlResourceOwnerDetails' => 'https://login.aaiedu.hr/sso/module.php/oidc/userinfo.php', 'publicKey' => 'file://' . __DIR__ . '/../storage/idp.crt', ], [ 'signer' => new \Lcobucci\JWT\Signer\Rsa\Sha256() ] ); $oidcStateSessionKey = 'OIDC_STATE'; |
<?php require 'bootstrap.php'; // TODO In real app, check if the user is already authenticated locally. // If so, prevent further processing, show warning message, redirect to another page, or similar. // Do the OIDC authentication. // We must set desired scopes here, otherwise 'openid' scope will be used by default. $redirectUrl = $oidcClient->getAuthorizationUrl(['scope' => 'openid profile hrEduPersonUniqueNumber']); // Save state so we can validate it later. $_SESSION[$oidcStateSessionKey] = $oidcClient->getState(); // Do the authorization request... header('Location: ' . $redirectUrl); exit(); |
<?php require 'bootstrap.php'; // TODO In real app, check if the user is already authenticated locally. // If so, prevent further processing, show warning message, redirect to another page, or similar. // We have to manually validate state if (!isset($_GET['state']) || (! isset($_SESSION[$oidcStateSessionKey])) || $_GET['state'] != $_SESSION[$oidcStateSessionKey]) { throw new \Exception('State parameter not valid.'); } // State parameter is validated. We can remove it so it can't be reused (and this page can't be refreshed). unset($_SESSION[$oidcStateSessionKey]); // Get tokens... try { $token = $oidcClient->getAccessToken('authorization_code', [ 'code' => $_GET['code'] ]); } catch (\Exception $e) { // TODO In real app log error, show error message, redirect to another page, or similar. throw $e; } // Use appropriate methods to get user data. // TODO In real app, log in the user locally, show success message, or similar. $accessToken = $token->getToken(); $refreshToken = $token->getRefreshToken(); $expires = $token->getExpires(); $hasExpired = $token->hasExpired(); $idToken = $token->getIdToken(); $email = $idToken->getClaim('email', false); $allClaims = $idToken->getClaims(); var_dump($allClaims); |
Repozitorij paketa: https://github.com/IdentityModel/oidc-client-js
Paket je moguće instalirati pomoću alata 'npm' ili je moguće koristiti kompiliranu verziju dostupnu u mapi (eng. folder) 'dist'. Zbog mogućnosti različitog načina organiziranja JS koda te zbog tipično većeg broja potrebnih JS i HTML datoteka prilikom dizajniranja aplikacija, u nastavku je naveden samo dio koda relevantan za obavljanje OIDC autentikacije. Paket omogućuje korištenje tijeka autorizacijskog koda za javne klijente pomoću PKCE parametara, pa će biti iskorišten takav način autentikacije (takav način je preporučen umjesto implicitnog tijeka).
// TODO Make 'UserManager' class available in current context // Example config to use for UserManager instance creation const config = { authority: "https://login.aaiedu.hr/.well-known/openid-configuration", client_id: "YOUR_CLIENT_ID", redirect_uri: "https://your-app.example.org/callback", response_type : "code", scope: "openid profile hrEduPersonUniqueNumber", loadUserInfo: false // Prevent additional request to userinfo endpoint, since all inforomation is in ID token }; const userManager = new UserManager(config); // Get user from local storage, if available (session storage is used by default). // Local user will be available if the authentication process was already performed. let user = null; userManager.getUser().then(localUser => {user = localUser}); // Method used to initiate an authentication process. This will trigger a redirect // to the authentication server (authorization request). userManager.signinRedirect(); // Method used to handle incoming authorization code on the callback URI, and then to fetch tokens. // The result is user object. userManager.getUser().then(authenticatedUser => {user = authenticatedUser}); // User object contains tokens, claims... console.log(user, user.profile) |