17.04.2018 | Joonas Westlin

Opt-in-toiminnallisuudet eivät ole erittäin hyvin tuettuja Azure AD:ssa (erityisesti v1-endpointissa). Tässä artikkelissa tutkimme kuinka voisimme toteuttaa sellaisen hyvällä tavalla. Aloitetaan kuvauksella käyttötapauksesta, jotta ymmärrät millaista toiminnallisuutta olemme tekemässä ja mitä ongelmia siihen liittyy.

KÄYTTÖTAPAUS

ContractZen on SaaS-sovellus sopimusten, kokousten ja yhtiön asiakirjojen hallintaan, jota me kehitämme yhteistyössä Helsinkiläisen startupin, ContractZenin kanssa. Lue lisää ContractZenistä asiakas-sivulta ja ContractZenin kotisivuilta.

Yksi usein pyydetty toiminnallisuus oli saada muistutukset esim. sopimusten vanhenemisesta käyttäjien Outlook-kalentereihin Office 365:en. Näin käyttäjät voivat saada muistutuksia heille tärkeistä asioista suoraan Outlookissa.

Eli meidän piti lisätä kalenteri-integraatio, jonka käyttäjät voisivat ottaa halutessaan käyttöön. Taustaprosessi lisäisi tärkeät tapahtumat käyttäjien kalentereihin.

Ongelma on, että kaikki sovelluksen vaatimat oikeudet Azure AD:ssa pitää hyväksyä täysin. Tämän toiminnallisuuden pitäisi olla valinnainen, mutta jos vaadimme pääsyn kalenteriin olemassa olevassa sovelluksessa, jokaisen käyttäjän pitää hyväksyä se. Jos emme käytä käyttäjän kalenteria, meidän ei pitäisi pyytää oikeutta siihen.

EHDOTETTU RATKAISU

Ehdotamme seuraavaa ratkaisua ongelmaan. Luodaan toinen sovellus Azure AD:en kalenteri-integraatiota varten. Vaadimme ainoastaan delegoidun oikeuden kalentereiden lukemiseen ja kirjoittamiseen Microsoft Graph API:n kautta siinä. Koska tätä oikeutta ei vaadita ContractZenin pääsovelluksessa, käyttäjien ei tarvitse vakiona antaa sovellukselle pääsyä heidän kalentereihinsa.

Mutta miksi delegoitu oikeus? Koska sovellus-oikeudet (application permissions) ovat organisaation laajuisia. Jos olisimme käyttäneet niitä, sovellus vaatisi pääsyn jokaisen käyttäjän kalenteriin heidän organisaatiossaan. Haluamme minimoida pääsyoikeudet sovellukselle. Vähimpien oikeuksien periaate on tärkeä.

On kuitenkin suositeltavaa käyttää sovellus-oikeuksia jos on kriittistä että sovelluksellasi on jatkuva pääsy. Delegoitujen oikeuksien mukana tulee myös muutamia huonoja puolia, esim. pääsy rajapintaan voidaan menettää erinäisistä syistä. Jos sovelluksesi ei voi käsitellä tätä ongelmaa, sinun pitää käyttää sovellus-oikeuksia. Tässä tapauksessa jos menetämme pääsyn, ContractZen voi tiedottaa käyttäjää ongelmasta, ja kun käyttäjä antaa oikeudet uudelleen, voimme ajaa täyden synkronoinnin joka tuo käyttäjän kalenterin taas ajan tasalle.

Jos käytät Azure AD:n v2-endpointia, voit käyttää dynaamista consentia toisen sovelluksen sijaan vaatiaksesi pääsyn kalenteriin myöhemmin. Se mahdollistaa minimaalisten oikeuksien vaatimisen aluksi, mutta mahdollistaa lisäoikeuksien pyytämisen myöhemmin. ContractZen käyttää v1-endpointia, joka ei tue dynaamista consentia.

SOVELLUKSEN LUOMINEN

Loimme multi-tenant-sovelluksen Azure AD:en, ja vaadimme delegoidun oikeuden: Have full access to user calendars.

Oikeudet MS Graph API:in

Huomaa, että oikealla sanotaan No. Tämä tarkoittaa, että oikeuden voi antaa tavallinen käyttäjä, eikä tarvitse ylläpitäjän hyväksyntää.

Meidän piti myös määrittää reply URL:t, jotta ne täsmäsivät jokaisen ympäristön käyttämiä osoitteita. Sinun pitää myös luoda avain, jotta sovelluksesi voi hakea access tokeneita.

TOIMINNALLISUUDEN LISÄÄMINEN

Toiminnallisuuden toteuttamiseksi lisäsimme napin ohjeiden kera käyttäjän asetukset-sivulle. Tämän napin painaminen ohjaa käyttäjän kirjautumaan sisään tiliinsä. Kirjautumisosoite voi näyttää esim. tältä:

https://login.microsoftonline.com/common/oauth2/authorize?client_id=00000000-0000-0000-0000-000000000000&response_type=code+id_token&response_mode=form_post&prompt=consent&msafed=0&redirect_uri=https%3a%2f%2fmyapp.com%2fCalendarSyncAuthorize&nonce=00000000-0000-0000-0000-000000000000

Käytämme totta kai auktoriteettia https://login.microsoftonline.com/common. Näin käyttäjät voivat kirjautua minkä tahansa organisaation tunnuksella. Lyhyt selitys parametreista:

  • client_id: Kalenteri-integraatiosovelluksen tunniste Azure AD:ssa
  • response_type: Määrittää, että haluamme authorisaatio-koodin sekä id tokenin vastauksessa
  • response_mode: Haluamme vastauksen HTTP POST:na (GET:n sijaan)
  • prompt: Vaatii käyttäjää hyväksymään oikeudet
  • msafed: Poistaa tuen Microsoft-tileiltä jotka on lisätty Azure AD:en
  • redirect_uri: Mihin vastaus lähetetään kirjautumisen jälkeen
  • nonce: Jotain mikä tekee tästä pyynnöstä uniikin, ja voimme sen avulla tarkistaa että vastaus on tosiaan tulos tästä pyynnöstä. Tulee olla uniikki joka pyynnölle.

Nonce lisätään myös cookiessa käyttäjälle.

Kun käyttäjä tulee takaisin, ensimmäinen asia joka pitää tehdä on validoida Id token. Tehdäksemme sen, tarvitsemme julkiset avaimet avainparista jota Azure AD käyttää allekirjoituksiin. Löytääksemme nämä avaimet, sovelluksesi voi hakea ne käynnistyksen yhteydessä hakemalla ensin manifestin osoitteesta https://login.microsoftonline.com/common/.well-known/openid-configuration. Tästä manifestista voi sitten etsiä jwks_uri-propertyn, ja hakea avaimet sisältävä dokumentti annetusta osoitteesta. Tällä hetkellä URL on https://login.microsoftonline.com/common/discovery/keys. Tokenit joita Azure AD tuottaa ovat allekirjoitettuja yhdellä niistä avaimista, ja tokenin header-osio kertoo millä.

.NET-sovelluksessa voit käyttää kahta kirjastoa: System.IdentityModel.Tokens.Jwt ja Microsoft.IdentityModel.Protocols.OpenIdConnect. Nämä voivat hakea avaimet ja validoida tokenit. Tämä artikkeli menee syvemmälle kuinka se tehdään: Manually validating a JWT using .NET.

Id tokenista voimme hakea noncen, ja verrata sitä nonceen joka on cookiessa. Jos nämä eivät täsmää, meidän pitää hylätä pyyntö. Cookie pitää myös poistaa, jotta sitä ei vahingossa lueta uudestaan.

Nyt kun meillä on validi vastaus, voimme hakea access tokenin ja refresh tokenin vaihtamalla authorisaatio-koodin niihin. Voit nähdä kuinka authorization code/hybrid-flow toimii dokumentaatiossa. Access token jonka saamme vastauksena ei ole tärkeä tässä kohtaa. Oikeastaan tarvitsemme vain refresh tokenin, jotta taustaprosessimme voi hakea uusia access tokeneita tarpeen mukaan. Meidän pitää varastoida refresh token jonnekin turvalliseen paikkaan. Esimerkiksi Azure Key Vault voi olla hyvä vaihtoehto tähän.

Meidän pitää myös hakea vähän tietoa Id tokenista. Jotta voimme lisätä kalenteritapahtumia käyttäjälle, tarvitsemme hänen tenant id:nsä ja object id:nsä. Nämä arvot eivät koskaan muutu, ja uniikisti tunnistavat käyttäjän. Sinun ei kannata käyttää kirjautumisnimeä (user principal name), koska se voidaan vaihtaa. Tenant id (joka tunnistaa organisaation), löytyy claimista tid; tosin .NET ClaimsPrincipal-objektissa sen tyyppi on http://schemas.microsoft.com/identity/claims/tenantid. Käyttäjän object id puolestaan löytyy oid-claimista, tai http://schemas.microsoft.com/identity/claims/objectidentifier-claimista.

Nyt kun tämä on kaikki tehty, olemme valmiit aloittamaan kalenteritapahtumien luomisen käyttäjille.

KALENTERIEN PÄIVITYS TAUSTALLA

Nyt kun taustaprosessimme ajetaan ajoittain, se voi hakea käyttäjät jotka ovat ottaneet toiminnallisuuden käyttöön tietokannastamme. Sen työnkulku on melko yksinkertainen:

  1. Hae refresh token käyttäjälle turvasäilöstä
  2. Hae access token
  3. Päivitä kalenteri
  4. Toista askeleet 1-3 lopuille käyttäjille

Mutta, voimme törmätä ongelmiin. Refresh token voi lakata toimimasta monesta syystä. Yksi niistä on jos käyttäjä vaihtaa salasanansa. Tästä syystä sovelluksesi tarvitsee sovellus-oikeudet, jos toiminnallisuus on kriittinen.

Joten meidän pitää olla valmiit siihen kun tämä tapahtuu. Jos tämä tapahtuu, meidän pitää tehdä vähintään seuraavasti:

  1. Asettaa flägi käyttäjälle, joka sanoo että hänen tokeninsa ei toimi
    • Jotta päivityksiä ei yritetä uudelleen ennen kuin ongelma on korjattu
  2. Ilmoittaa käyttäjälle ongelmasta, ja kertoa heille kuinka korjata se
    • Tämä voi olla esimerkiksi sähköposti

Tilanteen korjaaminen on melko yksinkertaista. Käyttäjän pitää vain mennä saman prosessin läpi jolla toiminnallisuus otettiin käyttöön alunperin. Sovellus saa uuden refresh tokenin, ja kaikki voi toimia taas.

Ainoa ongelma on kuinka palauttaa mitä menetettiin sinä aikana kun token ei toiminut. Auttaa huomattavasti jos taustaprosessi on idempotentti, eli sen tulos on sama riippumatta siitä kuinka monta kertaa se ajetaan. Siinä tapauksessa voimme ajaa sen välittömästi, tai antaa tilanteen korjata itsensä seuraavan ajon yhteydessä.

Toinen tärkeä asia joka pitää muistaa on refresh tokenien vanheneminen. Refresh tokenit vanhenevat jos niitä ei käytetä; vakiona 90 päivän päästä. Voit nähdä vakio elinajat tokeneille dokumentaatiossa.

Mutta joka kerta kun ajamme taustaprosessin ja haemme access tokenin, saamme uuden refresh tokenin. Kun saamme sen, voimme korvata vanhan sillä. Jos taustaprosessi ajetaan ajoittain, tämän ei pitäisi olla ongelma. Mutta jos on mahdollista että refresh tokenia ei käytetä kolmeen kuukauteen, ajastettu prosessi joka päivittää refresh tokenit on hyvä idea.

YHTEENVETO

Käyttämällä erillistä rekisteröityä sovellusta Azure AD:ssa teemme toiminnallisuudesta todellakin opt-inin. Delegoidut oikeudet mahdollistavat operoimisen minimaalisilla käyttöoikeuksilla. Monta asiaa voi mennä pieleen toteutuksessa, ja toivomme että tämä artikkeli auttaa sinua tulevissa koettelemuksissasi.

Jos käyttäisimme Azure AD:n v2-endpointia, voisimme hyödyntää sen dynaamista consentia saadaksemme pääsyn kalenteriin käyttämällä vain yhtä sovellusta Azure AD:ssa. Muutoin lähestyminen olisi samantapainen.