Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Uvod

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 (redoslijed 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ć 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, a rezultat su informacije o autenticiranom korisniku. Da biste dobili dojam kako to može na kraju izgledati, svakako bacite pogled na primjere implementacije.

Table of Contents

Registar resura

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. Detaljne upute o tome kako registrirati resurs dostupne su na stranici Sustav jedinstvene autentikacije korisnika Registar resursa.

Registracijom resursa i OIDC klijenta definirat će se:

  • općenite informacije o resursu, tj. usluzi
  • OIDC klijentske vjerodajnice:
    • ID klijenta (eng. client ID) - niz znakova koji jedinstveno određuje klijenta
    • tajni ključ klijenta (eng. client secret) - tajni niz znakova kojeg klijent koristi tijekom autentikacije krajnjeg korisnika (ne smije biti javno dostupan)
  • tip OIDC klijenta - naznaka je li klijent povjerljiv (eng. confidential) ili javan (eng. public). Povjerljiv klijent je onaj koji može sigurno čuvati klijentske vjerodajnice tj. tajni ključ klijenta. To su npr. web aplikacije koje imaju dio aplikacije koji se izvršava na poslužitelju (eng. backend), pa se klijentske vjerodajnice mogu čuvati tako da im samo razvijatelji aplikacije imaju pristup. Javni klijent je onaj koji ne može sigurno čuvati klijentske vjerodajnice tj. tajni ključ klijenta (engl. client secret). To su npr. web aplikacije koje se izvršavaju u web pregledniku (npr. JavaScript aplikacije), te sve nativne aplikacije (Windows, Linux, Android, iOS...), jer se pretpostavlja da postoji mogućnost izvlačenja klijentskih vjerodajnica iz takvih aplikacija.
  • lokacija za preusmjeravanje (eng. redirect URI) - lokacija na koju će poslužitelj vratiti odgovor na zahtjev za autentikacijom
  • opsezi (eng. scopes) - definicija skupa tvrdnji (eng. claims), tj. korisničkih atributa koji će se isporučivati nakon uspješne autentikacije
  • opis razloga korištenja traženih opsega i pripadajućih tvrdnji (korisničkih atributa)

Pod OIDC klijentom podrazumijeva se aplikacija, dio aplikacije, modul, biblioteka ili slično, koja podnosi zahtjeve za autentikacijom krajnjeg korisnika na autentikacijski poslužitelj i obrađuje odgovore iz kojih dohvaća korisničke podatke. Nakon što obavite registraciju resursa i OIDC klijenta, možete iskoristiti vrijednosti definirane u Registru resursa za konfiguriranje OIDC klijenta u vašem izvornom kodu (postavljenje ID klijenta, tajnog ključa, lokacije za preusmjeravanje i opsega).

Info

Pošto je u postupku registracije OIDC klijenta potrebno odabrati određene opsege kojima se definiraju korisnički atributi koji će se isporučivati, preporučamo da svakako proučite poglavlje "Opsezi i tvrdnje (eng. Scopes and Claims)" ovih uputa.

AAI@EduHr OIDC konfiguracija

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:

  • krajnje točke (eng. endpoints) koje se koriste za autentikaciju krajnjeg korisnika - svojstva 'authorization_endpoint', 'token_endpoint' i 'userinfo_endpoint'
  • URL na JSON web skup ključeva (eng. JSON Web Key Set - JWKS) pomoću kojih se može provjeravati potpis u ID tokenu - svojstvo 'jwks_uri'
  • podržani opsezi - svojstvo 'scopes_supported'
  • podržani autentikacijski tijekovi - svojstvo 'response_types_supported'
  • podržani tipovi identifikatora subjekta (eng. subject identifier) - svojstvo 'subject_types_supported'
  • podržani algoritmi za potpisivanje ID tokena - svojstvo 'id_token_signing_alg_values_supported'
  • podržane metode za generiranje parametra 'code_challenge' - svojstvo 'code_challenge_methods_supported'

Primjer sadržaja AAI@EduHr konfiguracije (dio vrijednosti u svojstvu 'scopes_supported' maknut radi preglednosti):

Code Block
languagejs
titleAAI@EduHr OIDC konfiguracjia
{
  "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"
  ]
}

JSON web skup ključeva

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:

SvojstvoOpis,,,,,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 je li 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:

Code Block
languagejs
titleJWKS
{
  "keys": [
    {
      "kty": "RSA",
      "n": "py7oHZwX71g3azW6La-cLUi-...-DS3FyOJnyY8bZS3SQ",
      "e": "AQAB",
      "kid": "69d8c46574",
      "use": "sig", 
      "alg": "RS256"
    }
  ]
}


Info

U slučaju nemogućnosti korištenja ključeva u formatu JWK, javni ključ u formatu PEM dostupan je na poveznici: https://login.aaiedu.hr/sso/module.php/saml/idp/certs.php/idp.crt

Trajanje kodova i tokena

Trajanje kodova i tokena je kako slijedi:

  • autorizacijski kod (eng. authorization code): 1 minuta
  • pristupni token (eng. access token) i ID token: 1 sat
  • token za osvježavanje (eng. refresh token): 8 sati

Testno okruženje AAI@EduHr Lab

Za testno okruženje OIDC klijent je potrebno podesiti prema uputama na stranici: Upute za korištenje AAI@EduHr Laba

Opsezi i tvrdnje (eng. Scopes and Claims)

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 zahtijevati 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' endpoint).

AAI@EduHr podržava korištenje standardnih OIDC opsega, 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

Standardni OIDC opsezi su:

  • openid
  • profile
  • email
  • address
  • phone

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 korisnič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 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 opseg 'profile'

OIDC tvrdnjaAAI@EduHr korisnički atributNapomena
namecn
family_namesn
given_namegivenName
middle_name
Nije podržano.
nicknamedisplayName
preferred_usernamehrEduPersonUniqueID
profilelabeledURI
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 opseg 'email'

OIDC tvrdnjaAAI@EduHr korisnički atributNapomena
emailmail
email_verified
Nije podržano.

OIDC opseg 'address'

OIDC tvrdnjaAAI@EduHr korisnički atributNapomena
address
Nije podržano.

OIDC opseg 'phone'

OIDC tvrdnjaAAI@EduHr korisnički atributNapomena
phone_numbermobile, telephoneNumber

Ako je dostupan atribut 'mobile', koristi se njegova vrijednost. Inače, ako je dostupan atribut 'telephoneNumber', koristi se njegova vrijednost.

OIDC preporučuje format E.164, dok AAI@EduHR koristi format E.123. Tvrdnja se ipak isporučuje, jer se radi o preporučenom formatu, a ne zahtijevanom.

phone_number_verified
Nije podržano.

AAI@EduHr opsezi i tvrdnje

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 može li AAI@EduHr korisnički atribut kojeg predstavlja imati višestruke vrijednosti ili ne.

Slijedi popis AAI@EduHr opsega, tvrdnji te pripadajućih AAI@EduHr korisničkih atributa.

AAI@EduHr opsegAAI@EduHr tvrdnjaAAI@EduHr korisnički atribut
hrEduPersonUniqueIDhrEduPersonUniqueIDhrEduPersonUniqueID
uiduiduid
cncncn
snsnsn
givenNamegivenNamegivenName
mailmailmail
telephoneNumbertelephoneNumbertelephoneNumber
hrEduPersonExtensionNumberhrEduPersonExtensionNumberhrEduPersonExtensionNumber
mobilemobilemobile
facsimileTelephoneNumberfacsimileTelephoneNumberfacsimileTelephoneNumber
hrEduPersonUniqueNumberhrEduPersonUniqueNumberhrEduPersonUniqueNumber
hrEduPersonOIBhrEduPersonOIBhrEduPersonOIB
hrEduPersonDateOfBirthhrEduPersonDateOfBirthhrEduPersonDateOfBirth
hrEduPersonGenderhrEduPersonGenderhrEduPersonGender
jpegPhotojpegPhotojpegPhoto
userCertificateuserCertificateuserCertificate
labeledURIlabeledURIlabeledURI
hrEduPersonProfessionalStatushrEduPersonProfessionalStatushrEduPersonProfessionalStatus
hrEduPersonAcademicStatushrEduPersonAcademicStatushrEduPersonAcademicStatus
hrEduPersonScienceAreahrEduPersonScienceAreahrEduPersonScienceArea
hrEduPersonAffiliationhrEduPersonAffiliationhrEduPersonAffiliation
hrEduPersonPrimaryAffiliationhrEduPersonPrimaryAffiliationhrEduPersonPrimaryAffiliation
hrEduPersonStudentCategoryhrEduPersonStudentCategoryhrEduPersonStudentCategory
hrEduPersonExpireDatehrEduPersonExpireDatehrEduPersonExpireDate
hrEduPersonTitlehrEduPersonTitlehrEduPersonTitle
hrEduPersonRolehrEduPersonRolehrEduPersonRole
hrEduPersonStaffCategoryhrEduPersonStaffCategoryhrEduPersonStaffCategory
hrEduPersonGroupMemberhrEduPersonGroupMemberhrEduPersonGroupMember
ooo
hrEduPersonHomeOrghrEduPersonHomeOrghrEduPersonHomeOrg
ououou
roomNumberroomNumberroomNumber
postalAddresspostalAddresspostalAddress
lll
postalCodepostalCodepostalCode
streetstreetstreet
homePostalAddresshomePostalAddresshomePostalAddress
homeTelephoneNumberhomeTelephoneNumberhomeTelephoneNumber
hrEduPersonCommURIhrEduPersonCommURIhrEduPersonCommURI
hrEduPersonPrivacyhrEduPersonPrivacyhrEduPersonPrivacy
hrEduPersonPersistentIDhrEduPersonPersistentIDhrEduPersonPersistentID
displayNamedisplayNamedisplayName
schacUserPresenceIDschacUserPresenceIDschacUserPresenceID
hrEduPersonCardNumhrEduPersonCardNumhrEduPersonCardNum

Stalne tvrdnje

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:

TvrdnjaOpisNapomena
issIdentifikator izdavatelja (eng. issuer). Sadrži vrijednost AAI@EduHr identifikatora izdavatelja koji je inače dostupan na OIDC konfiguracijskom URL-u.
subIdentifikator subjekta (eng. subject identifier). Sadrži jedinstveni identifikator krajnjeg korisnika tj. korisnika koji se autenticirao.Sadrži vrijednost AAI@EduHr korisničkog atributa 'hrEduPersonPersistentID'.
audPublika (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.
iatTvrdnja '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.
nbfTvrdnja 'ne prije' (eng. not before). Sadrži vremensku oznaku** (eng. timestamp) prije koje ID token ne smije biti prihvaćen.
nonceVrijednost tipa 'string' koja se koristi za povezivanje sjednice (eng. session) klijenta s 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 je li 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

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 (eng. header)
  • korisni podaci (eng. payload)
  • potpis (eng. signature)

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:

Code Block
titleID token
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY5ZDhjNDY1NzQifQ.eyJpc3MiOiJodHRwczovL2xvZ2luLmFhaWVkdS5ociIsImF1ZCI6IjZlNTUyOTUyMDk3ODJiN2IyIiwianRpIjoiYzQ0ZjRjZmZjYzg0Zjc5OTBmN2ExZDViMmMiLCJuYmYiOjE2MDI2NzQ0NzAsImV4cCI6MTYwMjY3NTA3MCwic3ViIjoiYmZhMTYwNWJlNDRhNTBhN2MiLCJpYXQiOjE2MDI2NzQ0NzAsIm5vbmNlIjoiZHRubWVCTDVIVm5oUWtJUiIsIm5hbWUiOiJJdmFuIEhvcnZhdCIsImZhbWlseV9uYW1lIjoiSG9ydmF0IiwiZ2l2ZW5fbmFtZSI6Ikl2YW4iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJpaG9ydmF0QHByaW1qZXIuaHIiLCJlbWFpbCI6Iml2YW4uaG9ydmF0QHByaW1qZXIuaHIiLCJockVkdVBlcnNvblVuaXF1ZU51bWJlciI6WyJMT0NBTF9OTzogMTIzNCIsIk9JQjogMTIzNDU2Nzg5MTIiLCJKTUJBRzogMTIzNDU2Nzg5MSJdfQ.c5q4W3bmcxQepAHj9n7xxj8WaH0wqIaiFkOFB9UE3joeVUyU9hWuNsDfPJ_BZ7WfYnhyMrv29Ys-dHs1oKqagvqFdk157IEwLnW1Dd5vNXvgGhMBhCTtseeQBDFS_DHN6KaksFDnGtFyWh3GXq-dWEBO86fpgSB3OuV9CD-AQAAbjXsN4Mz9MyaajMkhxWgxh_7HTZMlg5hACv3Xn-wiI8N3IGDqWrgdB87Vo6n0T5TfVaUZQ8mw5ca5fKy-aH5BP940LBb3bt6CvhZqxK0XFmD_M8hh-2MyRuPf2u6_P9HUORo1f8x8ZI30J2arhghPRcqEK9Sv_hdPxMy7WJJupg

Zaglavlje

Zaglavlje ID tokena sadrži sljedeće tvrdnje.

TvrdnjaOpisNapomena

typ

Tip tokena.Vrijednost je 'JWT'.
algAlgoritam korišten za potpisivanje tokena.Vrijednost je jedna od vrijednosti JSON svojstva 'id_token_signing_alg_values_supported' na OIDC konfiguracijskom URL-u.
kidID 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:

Code Block
languagejs
titleZaglavlje ID tokena
{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "69d8c46574"
}

Korisni podaci

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:

Code Block
languagejs
titleKorisni podaci u ID tokenu
{
  "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

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.

Info

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:

Code Block
titlePotpis ID tokena
c5q4W3bmcxQepAHj9n7xxj8WaH0wqIaiFkOFB9UE3joeVUyU9hWuNsDfPJ_BZ7WfYnhyMrv29Ys-dHs1oKqagvqFdk157IEwLnW1Dd5vNXvgGhMBhCTtseeQBDFS_DHN6KaksFDnGtFyWh3GXq-dWEBO86fpgSB3OuV9CD-AQAAbjXsN4Mz9MyaajMkhxWgxh_7HTZMlg5hACv3Xn-wiI8N3IGDqWrgdB87Vo6n0T5TfVaUZQ8mw5ca5fKy-aH5BP940LBb3bt6CvhZqxK0XFmD_M8hh-2MyRuPf2u6_P9HUORo1f8x8ZI30J2arhghPRcqEK9Sv_hdPxMy7WJJupg

Autentikacijski tijekovi

Podržani autentikacijski tijekovi (eng. flows) su:

  • Tijek autorizacijskog koda (eng. Authorization Code Flow) - OIDC ili OAuth2 tijek autorizacijskog koda
  • Implicitni tijek (eng. Implicit Flow) - OAuth2 implicitni tijek

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.

Info

AAI@EduHr preporučuje korištenje tijeka autorizacijskog koda za autentikaciju korisnika, neovisno o 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:

  • autorizacijska krajnja točka (eng. authorization endpoint) - URL je u svojstvu 'authorization_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u
  • token krajnja točka (eng. token endpoint) - URL je u svojstvu 'token_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u
  • krajnja točka za korisničke informacije (eng. userinfo endpoint) - URL je u svojstvu 'userinfo_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u

Ovisno o tome koji autentikacijski tijek se koristi, 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:

  • 'code' - definira korištenje tijeka autorizacijskog koda
  • 'token' - definira korištenje implicitnog OAuth2 tijeka

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.

Info

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:

  • id_token - OIDC implicitni tijek
  • id_token token - OIDC implicitni tijek
  • code id_token - OIDC hibridni tijek
  • code token - OIDC hibridni tijek
  • code id_token token - OIDC hibridni tijek
  • none

Tijek autorizacijskog koda

Tijek autorizacijskog koda je glavni i preporučeni način autenticiranja krajnjih korisnika koristeći protokol OIDC. Tijek autorizacijskog koda u protokolu OIDC je nadogradnja postojećeg tijeka autorizacijskog 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:

  • klijent napravi autorizacijski HTTP zahtjev preusmjeravanjem web preglednika na autorizacijsku krajnju točku
  • krajnji korisnik se autenticira
  • autentikacijski poslužitelj preusmjeravanjem web preglednika na registriranu lokaciju za preusmjeravanje (eng. redirect URI) šalje klijentu kratkotrajni (eng. short-lived) autorizacijski kod (kao GET parametar 'code')
  • klijent u pozadini (bez preusmjeravanja web preglednika) napravi HTTP zahtjev na token krajnju točku koristeći dobiveni autorizacijski kod, a u HTTP odgovoru dobije pristupni token (eng. access token), te ID token ako je korišten opseg 'openid' (što je preporučeno)

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.

1. HTTP zahtjev na autorizacijsku krajnju točku

Info

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 (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:

Code Block
languagetext
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:

ParametarVrijednostNapomena
response_type'code'Obavezan parametar. Vrijednost 'code' signalizira da se radi o tijeku autorizacijskog koda.
client_idID klijenta koji se dobije prilikom registracije klijenta.Obavezan parametar.
redirect_uriJedna od vrijednosti lokacija za preusmjeravanje koje su definirane prilikom registracije klijenta.Obavezan parametar.
scopeLista 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_methodJedna 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_challengeAko 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 pripremljen. Parametar 'code_verifier' se ne koristi u ovom zahtjevu, nego u idućem zahtjevu na token krajnju točku.
stateAko se koristi, vrijednost mora biti niz znakova s dovoljnom entropijom, dakle kriptografski siguran (ne smije ga biti lako pogoditi). Preporučuje 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). Tako se osigurava da na lokaciji za preusmjeravanje 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 krajnjeg 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).
nonceAko se koristi, vrijednost mora biti niz znakova s dovoljnom entropijom, dakle kriptografski siguran (ne smije ga biti lako pogoditi). Preporučuje 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). Tako se povezuje autorizacijski zahtjev s 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:

Code Block
languagetext
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}


Info

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.

2. HTTP odgovor sa autorizacijske krajnje točke

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).

Code Block
languagetext
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'

3. HTTP zahtjev na token krajnju točku

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 (ne koristi se preusmjeravanje web preglednika). URL token krajnje točke je dostupan u svojstvu 'token_endpoint' u JSON objektu na OIDC konfiguracijskom URL-u.

Code Block
languagetext
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:

ParametarVrijednostNapomena
grant_type'authorization_code'Vrijednost 'authorization_code' signalizira da se koristi autorizacijski kod u svrhu izdavanja tokena.
client_idID 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).
codeAutorizacijski 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 s dovoljnom entropijom, dakle kriptografski siguran (ne smije 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.

4. HTTP odgovor s token krajnje točke

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).

Code Block
languagetext
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
}

4.1 Validacija ID tokena

Ako je vraćen ID token, potrebno ga je provjeriti na način:

  • pomoću javnog ključa provjeriti ispravnost potpisa
  • provjeriti je li token istekao (vrijednost tvrdnje 'exp' - vrijeme isteka, eng. expiration time)
  • provjeriti može li se token početi koristiti (vrijednost tvrdnje 'nbf' - eng. not before)
  • provjeriti je li vrijednost tvrdnje 'aud' (publika, eng. audience) jednaka vrijednosti 'client_id' (ID token je namijenjen određenom klijentu)
  • provjeriti je li vrijednost tvrdnje 'iss' (izdavatelj, eng. issuer) jednaka vrijednosti svojstva 'issuer' u JSON objektu na OIDC konfiguracijskom URL-u
  • provjeriti je li vrijednost tvrdnje 'nonce' jednaka vrijednosti koja je poslana u prvom zahtjevu prema autorizacijskoj krajnjoj točki

Ako bilo koja provjera ne uspije, potrebno je odbaciti ID token i prekinuti autentikacijski tijek.

5. (opcionalno) HTTP zahtjev na krajnju točku za korisničke podatke

U slučaju da se u tijeku autorizacijskog koda ne korisiti opseg 'openid', dakle u slučaju kada se ne koristi mogućnost izdavanja ID tokena (što ne preporučujemo), korisnički podaci se mogu dohvatiti slanjem dodatnog HTTP zahtjeva na krajnju točku za korisničke podatke (eng. userinfo endpoint). 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).

Code Block
languagetext
GET {Userinfo Endpoint} HTTP/1.1
Authorization: Bearer {Access Token}
Host: {Authorization Server}

Primjer odgovora sa krajnje točke za korisničke podatke:

Code Block
languagetext
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"]}

OAuth2 Implicitni tijek

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 je li 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 endpoint). Ovaj tijek ne izdaje token za osvježavanje (eng. refresh token).

Info

Zbog veće razine sigurnosti, AAI@EduHr preporučuje 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:

  • klijent napravi autorizacijski HTTP zahtjev preusmjeravanjem web preglednika na autorizacijsku krajnju točku
  • krajnji korisnik se autenticira
  • autentikacijski poslužitelj preusmjeravanjem web preglednika na registriranu lokaciju za preusmjeravanje (eng. redirect URI) šalje klijentu pristupni token (eng. access token) kao fragment u URL-u (dio nakon znaka #)
  • klijent napravi HTTP zahtjev na krajnju točku za korisničke podatke pomoću pristupnog tokena

Slijedi opis pojedinih HTTP zahtjeva.

1. HTTP zahtjev na autorizacijsku krajnju točku

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 (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:

Code Block
languagetext
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:

ParametarVrijednostNapomena
response_type'token'Obavezan parametar. Vrijednost 'token' signalizira da se radi o implicitnom tijeku.
client_idID klijenta koji se dobije prilikom registracije klijenta.Obavezan parametar.
redirect_uriJedna od vrijednosti lokacija za preusmjeravanje koje su definirane prilikom registracije klijenta.Obavezan parametar.
scopeLista 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.

stateAko se koristi, vrijednost mora biti niz znakova s dovoljnom entropijom, dakle kriptografski siguran (ne smije ga biti lako pogoditi). Preporučuje 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:

Code Block
languagetext
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}

2. HTTP odgovor sa autorizacijske krajnje točke

Nakon što se krajnji korisnik autenticira, autentikacijski poslužitelj će generirati pristupni token i vratiti 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).

Code Block
languagetext
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'

3. HTTP zahtjev na krajnju točku za korisničke podatke

Korisnički podaci se dohvaćaju slanjem HTTP zahtjeva na krajnju točku za korisničke podatke (eng. userinfo endpoint). 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).

Code Block
languagetext
GET {Userinfo Endpoint} HTTP/1.1
Authorization: Bearer {Access Token}
Host: {Authorization Server}

Primjer odgovora s krajnje točke za korisničke podatke:

Code Block
languagetext
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

Tijek tokena za osvježavanje (eng. refresh token flow) omoguć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.

1. HTTP zahtjev na token krajnju točku

Opis potrebnih POST parametara:

ParametarVrijednostNapomena
grant_type

'refresh_token'

Obavezan parametar. Vrijednost 'refresh_token' signalizira da se radi o tijeku tokena za osvježavanje.
refresh_tokenVrijednost tokena za osvježavanje dobivena u tijeku autorizacijskog koda.Obavezan parametar.
client_idID klijenta koji se dobije prilikom registracije klijenta.Obavezan parametar.
client_secretTajni ključ klijenta koji se definira prilikom registracije klijenta.

Obavezan parametar.

scopeAko 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:

Code Block
languagetext
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
&scope={Scopes}                 // Opcionalno

2. HTTP odgovor s token krajnje točke

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, a 'exp' na novu vremensku oznaku isteka. Ostale tvrdnje će imati vrijednosti kao i u originalnom ID tokenu.

Primjer odgovora:

Code Block
languagetext
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
}

Primjeri implementacije

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:

  • postupak autentikacije pokrenut je automatski, dok bi se u stvarnim aplikacijama postupak autentikacije mogao pokretati klikom na gumb 'Prijava', 'AAI@EduHr prijava', ili slično
  • primjeri su napravljeni do trenutka dohvata korisničkih podataka, dok je u stvarnim aplikacijama dobivene korisničke podatke potrebno dodatno obraditi u smislu autorizacije, stvaranje korisničke sjednice (eng. session), ili slično
  • konfiguracijske postavke su tvrdo kodirane (eng. hardcoded), dok bi ih u stvarnim aplikacijama trebalo dohvaćati iz varijabli okruženja (eng. environment variables), ili slično

PHP

Svi primjeri PHP paketa se mogu instalirati pomoću alata Composer.

jumbojett / OpenID-Connect-PHP

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).

Code Block
languagephp
titleindex.php
linenumberstrue
<?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());

steverhoades / oauth2-openid-connect-client

Repozitorij paketa: https://github.com/steverhoades/oauth2-openid-connect-client

U primjeru postoje tri datoteke:

  • bootstrap.php - zajednički kod
  • index.php - pokretanje autentikacije slanjem autorizacijskog zahtjeva
  • callback.php - obrada dobivenog autorizacijskog koda i slanje zahtjeva za tokene

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).

Code Block
languagephp
titlebootstrap.php
linenumberstrue
<?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';


Code Block
languagephp
titleindex.php
linenumberstrue
<?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();


Code Block
languagephp
titlecallback.php
linenumberstrue
<?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);

cicnavi / oidc-client-php

Repozitorij paketa: https://github.com/cicnavi/oidc-client-php

Paket ima mogućnost automatskog dohvata OIDC konfiguracije sa OIDC konfiguracijskog URL-a (nije potrebno zasebno definirati krajnje točke), te automatskog dohvata i obnove javnog ključa iz JSON web skupa ključeva.

Primjer korištenja vidljiv je u README datoteci u repozitoriju samog paketa: https://github.com/cicnavi/oidc-client-php

JavaScript

IdentityModel / oidc-client-js

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 je iskorišten takav način autentikacije (takav način je preporučen umjesto implicitnog tijeka). 

Code Block
languagejs
// 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);

Više primjera dostupno je u repozitoriju paketa: https://github.com/IdentityModel/oidc-client-js/tree/dev/samples

.NET

Microsoft.AspNetCore.Authentication.OpenIdConnect

NuGet paket: https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.OpenIdConnect

Unutar aplikacije potrebno je instalirati nuget paket: Microsoft.AspNetCore.Authentication.OpenIdConnect. Paket može koristiti tijek aturizacijskog koda i ima mogućnost automatskog dohvata OIDC konfiguracije sa OIDC konfiguracijskog URL-a, uključujući dohvat javnog ključa iz JSON web skupa ključeva. Lokacija za preusmjeravanje mora biti tipa 'https://neka-domena.neki-tld/{opcionalna-putanja}/signin-oidc'. Dakle, 'signin-oidc' je obavezan na kraju, jer je to middleware za .NET koji odrađuje cijeli postupak autentikacije. Korištenje opsega 'openid' je obavezano.

Datoteka appsettings.json:

Code Block
titleappsettings.json
"OpenId": {
    "ClientId": "YOUR_CLIENT_ID",
    "ClientSecret": "YOUR_CLIENT_SECRET",
    "Authority": "https://login.aaiedu.hr/"
  }

Datoteke Startup.cs (kod koji nije relevantan za OIDC je uklonjen radi bolje čitljivosti):

Code Block
titleStartup.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
 
// *** 
public void ConfigureServices(IServiceCollection services)
{
    // ***
    services.AddAuthentication(options => {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(o =>
            {
                o.ResponseType = "code";
                o.ClientId = Configuration["OpenId:ClientId"];
                o.ClientSecret = Configuration["OpenId:ClientSecret"];
                o.Authority = Configuration["OpenId:Authority"];
                o.Scope.Add("openid");
                o.Scope.Add("hrEduPersonUniqueID");
                o.Scope.Add("cn");
                o.Scope.Add("mail");
                o.Scope.Add("hrEduPersonOIB");
                o.Scope.Add("hrEduPersonPersistentID");
                o.SignInScheme = "Cookies";
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateIssuerSigningKey = true
                };
            });
    services.AddAuthorization();
    // ***
}
 
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{          
    // ***
    app.UseAuthentication();
    app.UseAuthorization();
    // ***
}

Primjer dohvata korisničkih podataka:

Code Block
var principal = HttpContext.User as ClaimsPrincipal;
    
foreach (var claim in principal.Claims)
{
    Console.WriteLine("Claim: " + claim.Type + " : " + claim.Value);
}

// *** or...
string commonName = User.FindFirst("cn").Value;

Hvala kolegi Vidu Kašiću na suradnji u pripremi upute za paket Microsoft.AspNetCore.Authentication.OpenIdConnect.

Changelog

travanj 2021.

  • dodan opis korištenja u testnom okruženju AAI@EduHr Lab
  • dodan primjer implementacije u PHP-u pomoću paketa cicnavi/oidc-client-php

veljača 2021.

  • dodan primjer imlementacije u okruženju .NET pomoću NuGet paketa Microsoft.AspNetCore.Authentication.OpenIdConnect