Subsections of

Change history

2025-03-17: Quality focus

  • It is now possible to re-invite a user after removing all sign-in alternatives
  • Import Client configuration
  • Performance improvments for users with many interactions
  • New events:
  • Bug fix: Password handling for linked user accounts
  • Bug fix: 504-errors in admin portal caused by cookie
  • Bug fix: Reset password now works even if user was logged in where the reset link is used

2025-02-07: IP address improvments

  • Black and whitelistning of IP addresses (only APIs so far).
  • IP address information in events.
  • Improved handling of some edge cases for OTP.
  • Showing the identity number in listning of organisations.
  • Fixed a potential SQL Injection (CVSS 2.1 Low)
  • Fixed a potential Server-side Request Forgery for custom authentication alternatives (which requires special permissions) (CVSS 7.5 High)
  • Bug fix: Passwords created through invitations always performed breached password test even if instance is configured to not block.

2024-12-13: Geographic data and new database support

  • Improved UX for changing username and forgot password
  • Simplified 2FA step during sign-in
  • Added flexibility for Swedish BankId for some scenarios
  • Shortcut to add all functionalities in a group template
  • Groups can have a description and built-in has been split to built-in and requires at least one user.
  • Audits and session information is extended with geographic data.
  • Support for PostgreSQL when self-hosting (beta)

2024-10-11: Improved UX in sing-in scenarios

  • Improved display of errors with much better usability.
  • Extended e-mail validation. Catching common mistakes that can prevent a user from successfully creating an account.
  • Improved UX when using an old or used password reset link.
  • Added possibility to configure step-up authentication without auto-linking. This is helpful for scenarios where a stronger authentication is needed, but not associating the sign-in alternative with the user (effectively don’t display the sign-in alternative for new sign-ins).
  • Included user claims in export of personal data.
  • Only showing users that has access to module when impersonating.
  • Prevent export of external systems.
  • Search organisations that has a module activated.
  • A lot of API documentation have been added, primarly for the APIs most commonly used.

2024-09-23: Improved OTP and other minor changes

  • Improved one-time password input, for a better user experience.
  • More rate limits, for example for one-time passwords.
  • Displaying external group name in search groups.
  • Upgraded Font Awsome.
  • Upgraded Bootstrap in admin.
    • Removed several javascript dependencies, including jQuery.
  • Improved Open API specification (operationId has better names now).
  • Bug: Fixed so that it is possible to search with a “+” sign. For example this solves so that it is possible to search for e-mail addresses with “+” sign.

2024-08-20: Server sessions

  • Moved to Server sessions for users, which allows Authway to display current sessions and administrators can force a sign-out of a user.
    • Currently signed-in users will be automatically migrated.
  • Improved password input, which can display the password and warns about CAPS LOCK.
  • A sign-in with Swedish BankID will never be valid for more than one hour (because of legal agreement that don’t allow identity switching)
  • Rate limiting how many authroize request Authway allow for the same client and session. This is to stop clients that fails to handle a successful sign-in and therefor triggers a new sign-in, which in turn causes a never-ending loop for the user. Authway will return access_denied with “Request rate limit exceeded.” as description.
  • Support for Google Analytics/TagManager.

2024-07-17: Improved Compatibility

  • Automatically setting e-mail as verified when it matches the value from Microsoft, Apple, Google, or other social logins that provides the e-mail verified claim.
  • Handle ui_locales so that client/application can control the language in the sign-in flow.
  • Swedish BankID can’t be used to create new users by default (because of legal agreement that won’t allow such a user to use any other sign-in method).
  • Admin UI now uses backchannel sign-out, since front-channel logout is increasily broken because of browsers blocking third party cookies.
  • Authway now has a version number that will be the same over all instances (of course depending on the version currently running in the instance).

2024-06-03: UI improvments

  • Improved reset password flow
  • API and UI for viewing and management of user sign-in alternatives
  • Full support for BankId secure start.
  • Split self invite feature to, self invite and renew invite so that it is possible for a user to renew a send invitation, but not to make an invite from the sign-in flow.
  • All outgoing network traffic comes from a single IP to make it easier to configure IP restrictions in different scenarios, for example Webhooks.

2024-04-05: Small improvments

  • Support for copying a reset password link in Admin for users with special permission. This is to cover scenarios where the reset password email does not reach the user for some reason.
  • Extended some events with information about breached password usage.
  • A couple of bug fixes for edge scenarios and during heavy load.

2024-03-04: Skicka om verifieringskod

  • Möjlighet att skicka om verifieringskod i flöden där verifierad e-post/telefon krävs.
  • Förhindra att någon som gör en attack kan ta reda på om ett konto finns eller inte genom att alltid presentera lokal inloggning (gäller dock inte för kunder som tillåter att man istället skapar ett konto).

2024-02-01: Förbättringar i admin

  • Administrativt stöd för att lägga till en SAML-baserad inloggning för en organisation
  • Möjlighet att trycka ut en ny mallgrupp till alla organisationer som har tillgång till modulen. Detta kan bara göras en gång.
  • Stöd för åäö i användarnamnet (och därmed även i namnet för Externa system)
  • Förbättrad felinformation när uppgifter behöver kompletteras under auto-provision.

2023-12-21: Synkronisering av användarnamn

  • Även användaren synkroniseras nu med uppgifter från en ägar-specifik inloggning. Tidigare i höstas släppte vi stöd för att uppdatera personen, men nu utökas detta till att också omfatta användaren, vilket innebär att användarnamnet ändras när en person byter e-postadress.
  • Stöd för att exportera en klient.
  • Utökat klient-konfigurationen med möjlighet att sätta Url och Logga.

2023-12-07: Mindre förbättringar

  • Begränsat hur många gånger man kan begära återställ lösenord (per användare). Konfigurerbart, men vi tillåter användaren att göra det 5 gånger på 30 minuter. Denna begränsning förbättrar säkerheten.
  • Möjlighet att begränsa vilka inloggningssätt som default ska vara tillåtna för nya organisationer.
  • Stöd för att importera en modul igen (inställningarna skrivs då över) för att förenkla att ta förändringar mellan miljöer.
  • Gjort det möjligt att registrera flera externa grupper mot en grupp i Authway.
  • Nya händelser:
  • Kopiera LogId visas inte default, utan det måste slås på i miljöer där UserId inte loggas, utan en hash av UserId används.

2023-11-08: Säkerhetsförbättringar

  • Stöd för att kontrollera lösenord mot https://haveibeenpwned.com. Vi har nu möjlighet att kontrollera att lösenord inte finns med i världens största databas över läckta lösenord.
  • Finare kontroll över livslängden på länkar. Vi har nu ändrat så att länken i återställ lösenord är giltig i 6 h default (tidigare 24 h) och länken i inbjudningar är giltig i 72 h (tidigare 24 h) default.

2023-10-31: Länkade konton

  • Vi har nu stöd för att länka organisationskonton till ett personligt konto. Primärt för verksamheter som har privatpersoner som sin primära kund, men där denna person ibland behöver agera i kontexten av en organisation.
  • Förbättrad validering på klienten i inloggningen. Progressiv utökning av webbläsarens stöd.
  • Följer Microsofts och Googles varumärkesguidlines för “Logga in med Microsoft/Google”.
  • Förbättrat kontrast-förhållande på ramar och knappar i inloggningen för ökad WCAG-kompatibilitet.

2023-10-05: Automatiskt val av ägare och förbättringar för engångslösenord

  • När namnuppgifter krävs, men saknas på användare, kommer de samlas in i samband med inloggningen.
  • Vi visar (maskat) vart ett engångslösenord har skickats för att öka tydligheten.
  • Engångslösenord kan nu skickas om som e-post (om de har skickats som SMS och hamnat i skräpfilter).
  • Externa inloggningssätt sorteras i bokstavsordning.
  • När ett användarnamn matchar flera användare filtreras inloggningsalternativen utifrån vilka av användarna som har tillgång till modulen som inloggningen ska göras i. Detta minskar tillfällen när en användare behöver välja ägare eller att få meddelande om att tillgång saknas.

2023-08-25: Mindre förbättringar

  • Tagit bort kravet på att e-post anges när man återställer lösenordet.
  • Justerat texter för externa inloggningar för att få plats på mindre skärmar (lägre upplösning).
  • Säkerhetsförbättringar i admin-gränssnittet.
  • Bugg: Förbättrat felmeddelande om man försöker använda samma återställningslänk igen efter att lösenordet har återställts en gång.

2023-06-22: Förbättringar av situationer i inloggningen

  • Nu möjligt att återställa alla lösenord för användare som finns i flera organisationer (tidigare fick dessa bara återställningsmail för en av sina användare). Detta omfattar ett nytt e-postmeddelande som måste tas fram för alla med anpassade e-postmeddelanden.
  • Uppdaterar användarnamn/e-post för användare som skapats på ett användarnamn, men sedan byter i sin organisation.
  • Synkroniserar namn och e-post för personen från sin organisationsinloggning.
  • Ökat flexibiliteten i hur man kan hantera (lägga till/ta bort) claims för en användare i ClaimFilters.
  • Ny händelse:
    • UserUsernameChanged, vilket gör att förändringar av användarnamnet inte längre kommer med i UserUpdated.
  • API-förbättringar (och ändringar):
    • Exponerar export av personlig data (GDPR-stöd).
    • Möjlighet att ta bort alla rättigheter från en modul för en specifik användaren (GDPR-stöd).
    • API-nyckeln måste vara minst 12 tecken när ett webhook-system registreras.
  • Rättade buggar:
    • Ladda mer i ett sökresultat fungerade inte.
    • Det gick inte att uppdatera ett identitetsomfång.
    • Auth-uppgifterna sparades inte vid registrering av webhook (bara vid uppdatering)

2023-05-26: Förbättrad modulhantering för organisationer (ägare)

  • Förbättrat användargränssnitt för en organisations moduler
  • Möjlighet att sätta modulen som betald/obetald eller inaktivera den (i UI:t)
  • Rättigheter och/eller grupper kopplat till en modul tas bort när modulen inaktiveras för en organisation
  • Gruppmallar kan nu konfigureras att läggas till på existerande användare när modulen aktiveras
  • Nya händelser:
    • ModuleActivatedForOrganisation
    • ModuleInactivatedForOrganisation
    • ModuleUnpayedForOrganisation
    • ModulePayedForOrganisation
  • Förbättrad sortering av rättigheter i administrationen av grupper och externa system
  • Rättad bugg: Alla moduler (fler än 15) visas inte i admin för API-omfång
  • Rättad bugg: URL:en för modulen valideras inte, vilket gör att den kan anges utan protokoll som i sin tur ger följdfel.

2023-05-16: SAML utloggning och diverse småförbättringar

  • Ett låst konto låses upp direkt när användaren återställer lösenordet (förbättring)
  • Möjlighet att kopiera en inbjudningslänk för att dela den på annat sätt än att skicka inbjudningsmail
  • SAML Identity Provider stödjer nu även utloggning (även mellan protokollen)
  • Gjort det möjligt att specificera vilken tenant ett API-anrop gäller för genom att skicka X-IRM-TenantId HTTP Header (förutsatt att den som anropar har behörighet att konfigurera ägare).
  • Nya händelser:
    • ModuleWentOffline
    • ModuleWentOnline

2023-04-28: GDPR-förbätttringar

  • Fyra olika bakgrundsjobb för att ta bort information utifrån olika GDPR-scenarion. Borttag av tomma organisationer (Enskilda firmor innehåller personuppgifter), Borttag av användare som aldrig loggat in, Borttag av användare som inte har loggat in på lång tid och Borttag av användare som saknar behörigheter.
  • Skalbarhetsförbättringar i Webhook-leverans av händelser.
  • Förbättrade sidtitlar i inloggningsflödet (för bättre tillgänglighet)
  • Rättad bugg: Varumärkesanpassningen för en applikation tappas i slutet av återställ lösenord

2023-03-31: SAML Identity Provider

  • Authway kan nu agera som SAML Identity Provider i väldigt många situationer, även om det finns delar av SAML-standarden där vårt stöd kommer vidareutvecklas. I denna första uppdatering finns det stöd för att exponera/exportera SAML metadata, samt certifikatet som används, men i övrigt är inte administrativa möjligheter klara ännu, utan vi måste göra konfigurationen manuellt.
  • Fler varianter av namnkrav i samband med registrering av konto.
  • Förbättrad hantering av bekräfta e-post/mobil i samband med inloggning, primärt när användaren tar lång tid på sig att bekräfta.
  • Stöd för att välja BankId på samma enhet eller annan enhet direkt från applikationen
  • Ny händelse: FunctionalityDeleted
  • ClientId/Name i metadatat för fler händelser
  • Rättad bugg: Varumärkesanpassningen visas inte korrekt på sidan som bekräftar att lösenordet har återställts.

2023-02-24: Automatisk re-direct tillbaka till applikationen efter utloggning

  • Automatisk re-direct tillbaka till applikationen efter utloggning (om möjligt)
  • Två nya händelser: UserRoleAdded och UserRoleRemoved
  • Säkerhetsförbättringar och förbättringar i regelefterlevnad
  • Rättad bugg: CausedByPersonId sattes för ofta till User.Id istället för inloggad användare
  • Rättad bugg: Validering av GrantTypes för en klient

2023-01-18: Support för SSO tokens

  • Möjlighet för en klient att tvinga en single-sign-on av en användare
  • Rättad bugg: Möjligt att söka användare på användarnamn även för användare som inte har e-post som användarnamn
  • Rättad bugg: Möjligt att göra Impersonate på användare oavsett typ av användarnamn.
  • Rättad bugg: Användarnamnet på detaljer för en användare får rätt typ (utifrån ägarens inställningar)
  • Rättad bugg: Inställningar kan visas när man administrerar en annan organisation

2023-01-12: Validering av ägare vid inloggning

  • Tvingar användaren att logga in på nytt om klienten begär en annan ägare än den användaren tillhör
  • Tvingar användaren att logga in på nytt om användarens ägare inte har tillgång till applikationen
  • Förhindrar användaren att logga in i en applikation där modulen är satt till offline
  • Validerar att den tenant som klienten skickar med finns och tar annars bort den
  • Möjlighet att redigera Klientens anspråk (claims)
  • Rättad bugg: Tillåter ’ och : i namn

2022-12-16: Initiera skapa konto via OIDC

2022-12-08: Diverse mindre förbättringar

  • Inställning för att tillåta samma organisationsnummer på flera organisationer
  • Förhindra att användaren får fel vid dubbelklick på knapparna i inloggningsflödet
  • Stöd för att exportera API-resurs
  • Stöd för att kunna kundanpassa ännu fler texter
  • Stöd för att automatiskt underhålla SQL Server index
  • Rättat en del buggar, primärt buggar som orsakats av cachning

2022-11-10: Export och import

  • Stöd för att exportera modul och API-omfång
  • Stöd för att importera modul och API-omfång, men bara första gången. Uppdatering av existerande kommer senare.
  • Förbättrat möjligheten att prenumerera på fler händelser i ett topic (Webhooks)

2022-10-10: Stöd för Duende IdentityServer

  • Stödet för att gå över till Duende IdentityServer
  • Vissa anpassningar till prismodellen
  • Validerar omfång (scopes) när man sparar en klient (kräver Duende-övergång)
  • Information om cachning i Admin-gränssnitten (kräver Duende-övergång)
  • Stöd för isolering av API-resurser, dvs krav på att en access token måste vara unik för en resurs (kräver Duende-övergång och Enterprise-licens)
  • Uppdaterad information om an API-resurs (kräver Duende-övergång)
  • Funktionaliteter listas i bokstavsordning
  • Ett antal buggar är rättade

2022-08-25: Registrera konto

  • Stöd för att registrera nytt konto i Privatpersoner.
  • Stöd för att registrera nytt konto i organisation som matchats på domän.
  • Registeringsflödet tar hänsyn till befintliga inställningar som om e-post och/eller telefonnummer ska krävas, men har också tillfört nya inställningar i form av vilket krav som finns kring namn.
  • Registeringsflödet och auto-provision är samordnat och fungerar båda på samma sätt.

2022-08-10: Förenklat nytt lösenord

  • Tagit bort “Bekräfta lösenord”.
  • Gjort det tydligare vilka lösenordsregler som gäller och om de är uppfyllda.
  • Använder ägarspecifika lösenordsregler vid återställ lösenord.
  • Lyft upp externa inloggningar överst när man registrerar ett konto.
  • Gjort det enklare att helt anpassa visningen av villkor.

2022-07-07: Händelser för organisation och person

  • Händelser för en organisation (OrganisationCreated, OrganisationUpdated, OrganisationDeleted, OrganisaitonClaimAdded, OrganisationClaimRemoved, TrustedDomainAdded, TrustedDomainRemoved). Händelserna möjliggör nya integrationsscenarion.
  • Händelser för en person (PersonCreated, PersonUpdated, PersonDeleted). Händelserna möjliggör nya integrationsscenarion.
  • Stöd för att rensa bort händelserna när en person tas bort och lagring av händelserna används (GDPR).

2022-05-10: Stora prestanda och skalbarhetsförbättringar

  • Stora prestanda- och skalbarhetsförbättringar
  • Genomgång av alla log-egenskaper så att de använder samma namn
  • GDPR: Lagt till stöd för Log-id som kan användas vid loggning, där id:t inte går att översätta till databasens id. Detta för organisationer som vill ha ännu striktare applicering av GDPR-reglerna

2022-02-20:

  • Stöd för att samla på data från BankId-inloggning (och möjlighet att hämta detta via API)
  • Norska SMS-texter

2022-01-12: Förbättrat organisationsstöd

  • Stöd för att skapa flera ägare med samma namn om firman är en enskilda firma (kräver organisationsnummer)
  • Möjlighet för flera organisationer att skapa dotterbolag med samma namn
  • Diverse buggfixar och mindre utökningar i API:t

2021-12-20: Administration av klienter

  • Stöd för att lägga till nya klienter
  • Stöd för att redigera befintliga klienter
  • Gjort så att externa system går att filtrera bort under listningen av klienter
  • Fixat så att man inte kan sätta ett slutdatum innan dagens datum för hemligheter på klienter och resurser.
  • Listar klienter med RequireClientSecret samt utgångna hemligheter som ej tillgängliga.

2021-12-15: Automatiskt hamna på extern inloggning

  • Stöd för att automatiskt hamna på extern inloggning om Client bara tillåter ett inloggningssätt.
  • Stöd för att automatiskt hamna på extern inloggning om Ägaren bara tillåter ett inloggningssätt.
  • Ändrat så att bara förnamn eller efternamn krävs för en person.
  • Förbättrad säkerhet i Authway by IRM Cloud

2021-11-25: Diverse mindre förbättringar

  • Förenklat en del texter och mindre justeringar i utseendet.
  • Förbättringar i AutoLink när flera användare matchar.
  • Diverse buggfixar

2021-10-28: Stöd för händelser

  • Händelser för en användare (UserCreated, UserDeleted, UserInvited, UserLockedout, UserLoginAdded, UserLoginRemoved, UserPasswordChanged, UserPasswordRemoved, UserSignedIn, UserSignedOut, UserSignInAssociated, UserSignInFailed, UserUnlocked). Händelser möjliggör nya integrationsscenarion. I denna version har vi bara ändrat så att alla Audit görs utifrån händelserna, men fler interna förändringar kommer. Vi kommer även införa fler händelser framöver.
  • Ändrat så att IdP:n bara returnerar unika claims (typ+värde), vilket möjliggör scenarion där man sätter samma permission på flera funktionaliteter.
  • Uppdaterat Bootstrap till 4.6.0 (från 4.3.1)

2021-10-13: Stöd för att samla in e-postadresser/telefonnummer

  • När ett konto saknar e-postadress och/eller telefonnummer så kan dessa samlas in av tjänsten när den är konfigurerad att kräva uppgifterna.
  • Stöd för att i API:t skicka med extra parametrar i en inbjudan, vilket gör att man kan skicka med clientId så att användaren skickas till den applikationen efter att kontot är skapat.

2021-10-07: UI-anpassningar per klient och fler inställningar per ägare

  • Det är nu möjligt att ha egna stylesheets och egen logga för en klient, så att utseendet kan varumärkesanpassas.
  • Ändrat så att vi vet vilken klient som startade inloggningsflödet även vid återställning av lösenord (sedan tidigare fungerade det vid självinbjudan). På så sätt kan varumärkesanpassningar ligga kvar och vi kan skicka användare tillbaka till rätt applikation efter återställningen.
  • Lagt till möjlighet att ha kontaktinformation i inloggningsflödet, t.ex. för att användaren behöver kontakta någon vid problem.
  • Vi har gjort det möjligt att göra fler inställningar per ägare, t.ex. typ av användarnamn, regler för lokala lösenord, mm

2021-09-06: Buggfixar och bättre stöd för lösenordsregler

  • Vi genererar numera passwordrules-attribut för Nytt lösenord, så att lösenordshanterare kan skapa korrekta lösenord enligt de regler som finns.
  • Rättat några mindre buggar.
  • Förbättrad loggning inom vissa områden.

2021-08-20: Byte av e-post och telefon (användarnamn) via API och bekräfta e-post och/eller telefonnummer vid inloggning

  • I API:t finns nu stöd för att byta ut e-postadressen och/eller telefonnumret för användaren. Om uppgiften som ändras också används som användarnamn slår det igenom på det också.
  • API:t tillåter att man skickar in att en e-postadress/telefonnummer är verifierad redan.
  • Vi har utökat inloggningen med möjlighet att bekräfta e-postadressen och/eller telefonnumret i samband med att man loggar in. Detta behövs för att användaren ska kunna logga in efter att uppgiften ändrats om tjänsten kräver verifierade uppgifter.

2021-07-09: Utökat stöd för mer claims

  • API:t har stöd för att lägga till egna claims på en användare och dessa returneras till applikationer som begär dem
  • API:t har stöd för att lägga till egna claims på en organisation och dessa returneras till applikationer som begär dem

2021-06-20: Inloggning med engångslösenord

  • Stöd för inloggning med engångslösenord.
  • Bättre hantering när login_hint, idp och tenant skickas med till IdP:n från applikationen.
  • Rättat ett flertal buggar (mest i admin-gränssnittet).

2021-05-27: Autoprovision och admingränssnitt

  • Autoprovision av användare genom en extern inloggning (t.ex. Azure AD) som är konfigurerad för att automatiskt skapa användare. Om nödvändig information inte fås från den externa inloggningen ombeds användaren att ange de uppgifter som saknas.
  • Bekräfta konto genom e-post (behövs i vissa scenarion av autoprovision).
  • Administration av betrodda domäner för en ägare.
  • Administration av API-omfång (scope).
  • Administration av API-resurs.
  • AutoLink-stöd vilket innebär att för ägare med extern inloggning (t.ex. Azure AD) som är konfigurerad för att automatiskt skapa användare, så kan steget med att bjuda in en användare hoppas över. Dessa externa inloggningsalternativ kommer ändå presenteras och den externa inloggningen länkas automatiskt till den upplagda användaren. Dessa inloggningsalternativ visas även för befintliga användare även om de har ett annat inloggningssätt.
  • API:t för användare har nu stöd för telefonnummer (vid hämta, uppdatera och skapa)
  • Diverse mindre buggrättningar och förbättringar av den senaste tidens funktionalitet

2021-05-04: Möjlighet för ägaren att anpassa de tjänstegemensamma inloggningssätten

  • Ägaren kan själv styra vilka inloggningssätt som är tillåtna
  • Ägaren kan själv styra vad som ska stå på inloggningsknappen
  • Ägaren kan själv begränsa vilket Azure AD som ska användas
  • Ägaren kan själv lägga upp valfri OpenId Connect inloggning

2021-04-08: Flerspråkstöd vid inloggning

  • Inloggningsflöden stödjer nu både svenska och engelska.
  • Flerspråkstödet ger också möjligheten att anpassa alla texter genom att bara tillföra nya resursfiler för de textanpassningar som man vill göra.
  • Stöd för .NET 5
  • Stöd för att skicka “tenant:GUID” eller “tenant:unikUrl” som acr_values för att begränsa användarna till en specifik ägare.

2021-03-26: Kör som någon annan

  • Nu finns det stöd för användare som har behörighet att kunna köra som en annan användare (inom samma ägare eller över alla ägare, beroende på behörighet). Varje client måste slå på att de stödjer det och för Clients som inte stödjer det kommer orginalanvändaren (impersonator) att skickas. Admin-gränssnittet har stöd för att “Köra som” på användarna.
  • Förbättring av vilka amr-claims som skickas till applikationen, så det t.ex. blir enklare att avgöra om tvåstegsverifiering har använts.
  • Utökad auditing där vi nu loggar inloggningsmetod, inloggninskrav (1FA, 2FA eller SSO) och om inloggningen har gjorts i “Kör som” (det sista visas även för användaren).
  • Övergång från oid-claim (ownerid) till tid-claim (tenantid). Applikationer bör påbörja förändringen att också byta även om båda returneras under en övergångsperiod.
  • Admin loggar in med PKCE och “code” istället för “hybrid” för att öka säkerheten.

2021-03-15: Stöd för Identity Server 4.1 och Bootstrap 4

  • Uppgradering via migrering
  • Bättre anpassning till olika skärmstorlekar i admin
  • Organisationens namn visas i Sök person/användare (om organisationen har flera)

2021-02-02: Platsinformation för IP-adresser

  • Stöd för att hämta platsinformation för IP-adresser från ipstack.com eller från IP2Location Lite database.
  • Granskningsloggarna visar nu platsinformation om sådan finns tillgänglig.
  • Visar information om kraven på lösenord när man skapar sitt konto.

2020-12-11: Förbättrade Azure AD Integration

  • Förbättrad autoprovisioning av användare som loggar in med Azure AD genom att deras E-post betraktas som bekräftat förutsatt att den stämmer överens mellan Azure AD och Identitetstjänsten.
  • Synkronisering av användarens säkerhetsgrupper till externa roller
  • Rättat bugg så att förändringar i externa roller slår igenom direkt vid inloggningen (tidigare slog det igenom vid nästa inloggning).
  • Gjort så att person och användare alltid skapas tillsammans (går att slå av med inställningar).
  • Möjlighet att lägga till användarvillkor och/eller dataskyddspolicy när en användare ska registrera ett konto (GDPR).

2020-11-24: Tvåstegsverifiering, mm

  • Möjlighet att ansluta tvåstegsvefieringsappar som Google Authenticator och Microsoft Authenticator till ett konto för säkrare inloggning.
  • Möjlighet att skapa nya återställningskoder för användning om man tappar bort sin tvåstegsverfieringsapp eller inte har tillgång till den.
  • Administratörer kan ta bort tvåstegsverifieringsappen från en användare
  • Inloggning med tvåstegsverifieringsapp eller återställningskod
  • Information om hur en användare kan byta ut sin bild
  • Möjlighet för användaren att ladda ner den persondata som Identitetstjänsten har (GDPR)

2020-11-22: Diverse mindre förbättringar

  • API för att “köpa” moduler för en Tenant
  • Möjlighet att sätta att en gruppmall automatiskt ska läggas till för nya användare
  • Rättad bugg: Visar vad som är fel när en användare försöker återställa sitt lösenord (tidigare visades bara att något gick fel).

2020-11-13: Administrera en annan ägares användare, grupper och externa system

  • Från administrationssidan av en ägare kan man nu välja att köra som den ägaren, vilket ger möjlighet att göra alla administration av den ägarens användare, grupper och externa system

2020-10-30: Trusted subsystem

  • Gjort det möjligt för att system, som har loggat in en användare på annat sätt än Identitetstjänsten, att ändå hämta ut en Access Token för att kunna göra API-anrop till andra tjänster. Detta görs med en förtroende-relation mellan systemet och Identitetstjänsten.

2020-10-01: Stöd för MySQL

  • Fullt stöd för att köra tjänsten med MySQL som databas (utöver SQL Server/Azure SQL).

2020-09-15: Organisationsnummer

  • Lagt till organisationsnummer som en frivillig uppgift på organisationer
  • Organisationsnummer kan komma med som en claim
  • Lagt till möjligheten att ta bort en ägare

2020-08-31: Organisationshierarkier

  • Lagt till möjligheten att skapa organisationshierarkier (moderbolag/dotterbolag)
  • Lagt till möjligheten att välja organisation för en person (men bara om det finns en hierarki konfigurerad)

2020-08-15: Användarbilder

  • Lagt till API för att skapa en användarbild utifrån initialer (alltid med samma färg)
  • Lagt till så picture claim kan komma för användaren med rätt länk till Gravatar och fallback till bild med initialer, vilket gör det lätt att visa bilden i alla applikationer (inte bara admin)
  • API för att lägga till/ta bort behörigheter på en användare

2020-06-30: Begränsad admin av API-resurser

  • Det går att skapa, söka och ta bort en API-resurs
  • Grundläggande inställningar för API-resurser går nu att göra i admin-gränssnittet

2020-06-08: Förbättrat UX

  • Lagt till information om vilken kund man är inloggad på
  • Exempel för externa system (så det ska bli enklare att klara sig själv)
  • Bytt ut checkboxar mot Ja/Nej-knappar
  • Lagt till hjälpinformation (popover) på flera ställen
  • Diverse mindre buggrättningar

2020-05-15: Administration av moduler

  • Komplett stöd för att administrera moduler, inkl att hantera funktioner och gruppmallar.

2020-04-28: Lägga till/uppdatera ägare

  • Stöd för att söka, lägga till och uppdatera ägare

2020-04-21: UX-förbättringar

  • Uppdaterat admin-gränssnitten med flertal UX-föbättringar, bla för att göra det tydligare vad som just nu redigeras.
  • Gjort det möjlighet att anropa Authorize-endpoint som en API-resurs (utöver en Client)

2020-04-15: Externa system och moduler

  • Administration av ett externt system
  • API för att hämta begränsad modul-information som om den är online/offline samt sökvägar till olika moduler för att kunna länka (kräver ingen behörighet)
  • Begränsat stöd för att administrera grundinformation om moduler

2020-03-15: Förbättringar kring e-post

  • Bytt till .NET Core mail-stöd och avvecklat MailKit
  • Gjort det möjligt att lägga till olika typer av begräsningar i vilka adresser som e-post skickas till för att göra det säkrare i utveckling och test
  • Mindre buggrättningar

2020-02-13: Självinbjudan

  • Gjort det möjligt för en användare att skicka en inbjudan till sig själv (t.ex. om den som admin skickat har gått ut).
  • Förbättrat informationen till användare som inte har något konto än

2020-01-28: .NET Core 3.1

  • Visar alla användare som ingår i en grupp
  • Stöd för .NET Core 3.1

2019-12-12: Stöd för inloggning med SAML

  • Möjlighet att konfigurera en SAML-inloggning för en ägare
  • Möjlighet att logga in med SAML

Subsections of Get Started

Introduction to IAM

Identity and Access Management (IAM) systems ensure user identification and access to functionality and data in applications. IAM is a term that has long been used for an individual organization’s need to ensure that the right people have access to the right parts of the internal digital environment. Nowadays, an organization’s digital services extend to customers, suppliers and partners, which results in completely new requirements on IAM such as, to be able to connect and use these organizations’ own ability to identify and ensure access to the digital environment. This is often called Customer Identity and Access Management or shorter CIAM.

Basic Concepts

At the very core of CIAM is of course Identity, which is someone or somewhat that wants to get access to your digital resources, such as web applications, APIs and so on. Often the identity is a user account that represents an end-user such as a customer, an employee, a member and whoever you want to allow access, but the identity could also represent non-humans such as software (another system, robotics, Internet of Things devices).

Authentication is verifying who a user is (determine the identity) and Authorization is verifying what resources they are allowed to access. In CIAM it is also important to determine what organization (tenant) the user belongs too.

Authway by IRM enables many capabilities that your scenario most likely will need some of:

  • Smooth sign-in experience: A professional sign-in that works on all devices, with your brand’s look and feel.
  • Easy sign-up: The best sign-up is the one you don’t need to do at all and we support full provisioning of users from their enterprise identity providers, but for private individuals the sign-up is just as easy as the sign-in.
  • Multi-factor authentication (MFA): With many users’ credentials stolen it is more important than ever to require additional proof of identity.
  • Re-authentication: Require the user to authenticate once more before allowing them to perform sensitive operations.
  • Step-up authentication: Require a user to use a stronger proof of identity, such as a legal identity provider, for certain operations.
  • Role-based access control (RBAC): Create groups of users to ease the management of access privileges.
  • Attribute-based access control (ABAC): When supporting many different organizations need for control of access privileges it will fast become impractical to create a RBAC model that fits all. Instead it is better to verify access to functionalities and additional attributes like the organization (tenant) that the user belongs too.
  • Impersonate: Provide better customer support experience with the possibility to run your services as if you were the user.

Authentication and Authorization

Authentication is the process of verifying the user, but the challenge when doing this for customers, suppliers, partners and other organizations is that each organization has their existing infrastructure of doing that already. Instead of forcing the user’s of these organizations to create new identities it is more secure and convenient for them to re-use the identity they already have.

Identitytypes.png Identitytypes.png

Authway by IRM supports many Identity Providers

  • Local username and password
  • Social identity providers, such as Google, Twitter, Microsoft and many more
  • One-time passwords (OTP)
  • Legal identity providers, such as Swedish BankId, Freja or other
  • Enterprise providers, such as Microsoft Active Directory (on-prem or Azure)

The Identity providers can be enabled or disabled per tenant and it is even possible to add custom OIDC or SAML identity providers for a tenant.

Authorization is the process of determine what resources the identified user is allowed to access and there are many aspects of this. Some of this aspects is handled fully or partial handled by Authway, but there is always a responsibility on your applications to apply the logic and make the final call about what a user can access and not. Authway helps you with authorization by:

  • Control which organization (tenant) is allowed to use which modules. This allows you to split your systems into parts that different organizations should be allowed to use without you needing to do anything.
  • Configure build-in groups that will be created for each organization allowing you to use role-based access control (RBAC).
  • Configure functionalities that your system needs to verify access too and allowing the organizations to configure access to the functionalities in a way that fits their own organization without you needing to figure out a one-size fits all access control model.
  • It is even possible to limit access to a functionality to specific organizations (tenants), for example if you have created a custom report for a specific customer.
  • Makes it easy to filter information based on which organization (tenant) a user belongs too.
  • Determine how the user have signed in and require them to use a stronger identification for sensitive operations.
  • Require a user to re-authenticate before performing a sensitive operation.

Introduction to OIDC and OAuth

OAuth 2.0

OAuth 2.0 is a protocol used to access HTTP resources (API:s) on behalf of the user (resource owner) without sharing the user’s credentials. It’s the most commonly used standard for IAM today and used by large social media platforms as Facebook, Google and Twitter.

OpenID Connect

OpenID Connect (OIDC) is an identity layer on top of OAuth 2.0 that makes it easy to verify a user’s identity and retrieve basic information about the user from the identity provider. This allows applications to verify the identity of the end-user and retrieve this information as JSON web tokens (JWTs) using one of the grant types confirming to the OAuth 2.0 specification.

OAuth Grant Types and How to Choose?

The OAuth 2.0 protocol defines different grant types for obtaining access tokens:

Authway by IRM have support for all, except for Device Code (typically used on devices like televisions). Since we don’t support Device Code and both Implicit and Resource Owner Credentials are not recommended the choice is between Authorization Code or Client Credentials. To choose between these two the only question you need to answer is if it is an end-user that is signing in or if it is a system-to-system use case? For system-to-system access you should use Client Credentials and for end-user scenarios you should use Authorization Code, preferably with PKCE since it is more secure.

Explaining the Grant Types

Before explaining the grant types, let’s take a look at the different OAuth roles.

  • Resource Owner: Entity that can grant access to a protected resource. Typically, this is the end-user (User).
  • Resource Server: Server hosting the protected resources. This is the API you want to access.
  • Client: Application requesting access to a protected resource on behalf of the Resource Owner.
  • Authorization Server: Server that authenticates the Resource Owner and issues access tokens after getting proper authorization.

Client Credentials

The Client Credentials grant type is the simplest of all and it uses a client_id and client_secret credentials of a Client to authorize and access protected data from a Resource Server.

Client Credentials.png Client Credentials.png

  1. The client application makes a token request to the authorization server by providing the client credentials.
  2. The authorization server authenticates the client and issues an access token.
  3. The client application uses the access token when requesting resources from the resource server.

External systems

A challenge for a resource server that wants to support both system-to-system and end-user access tokens is that the contained claims in the tokens will be very different. For an end-user you’ll have claims for name, email, tid (tenant id), role and perm (permission), but a system-to-system access token will only have claims like client_id and client_name. This can be cumbersome to handle when validating access permissions and logging for example who have changed in data.

To make it easier for a resource server Authway have a concept called External system. An external system is a combination of a configured client and an user. Authway also overrides the standard token creation so that the token will include claims like name, tid and perm even in the Client Credentials grant.

Authorization Code (with PKCE)

The Authroization Code grant type is the most commonly used grant type to authorize the client and user access to protected data from a resource server. This uses browser redirections for communication and it provides some important security benefits such as the ability to do the authentication without the access token passing through the user-agent and potentially exposing it to others. This grant type can be used for SPA and native applications too, but in these scenarios it is usually with the access token in the user-agent. For SPA this can be avoided by creating a backend for frontend to increase the security.

Authorization Code.png Authorization Code.png

  1. Resource owner tries to access the client application via his user agent.
  2. The client application makes an authorization request to the authorization server.
  3. The authorization server authenticates the resource owner.
  4. Authorization server sends a temporary Authentication Code back to the client.
  5. The client application makes a token request to authorization server with the authorization code.
  6. If the token request is valid, the authorization server returns the access token, expiration time and a refresh token (if requested).
  7. Finally, the client application uses the access token when requesting resources from the resource server.

Refresh Token

The Refresh Token grant is used by clients to exchange a refresh token for an access token when the access token has expired. When an access token is obtained using authorization code grant type or client credentials grant type, to give additional security it has been designed to expire after the given token expiration time. In this case without end users interaction, authorization server will validate the refresh token and issue a new access token.

Glossary

A

Access Token

An Access Token is a piece of data that represents the authorization to access resources on behalf of the end-user. The access token is typically in the form of a JSON web token (JWT).

Authorization Server

Server that authenticates the Resource Owner and issues access tokens after getting proper authorization.

Auto-linking

The process of automatically associate an external identity provider with a user, which can be used to associate more than one identity provider with an user account and/or to remove the need to send an invitation to a user.

Auto-provision

The process of automatically create a user based on information from an external identity provider, which remove the need for any manual administration of users.

C

Claim

A statement (for example a name or an email address) about an entity (typically the user). Here is a list of supported claims.

Client

Application requesting access to a protected resource on behalf of the Resource Owner.

I

Identity Provider or Identity Server

Server that identifies an entity (typically the user) and issues token after proper authentication.

J

JSON web token (JWT)

JWTs contain claims (for example a name) about an entity (typically the user) plus some extra metadata.

L

Linked users

Linked users are users in different tenants where all of them will get the same sub claim (unique identifier).

O

OAuth 2.0

OAuth 2.0 is a protocol used to access HTTP resources (API:s) on behalf of the user (resource owner) without sharing the user’s credentials.

OpenID Connect

OpenID Connect (OIDC) is an identity layer on top of OAuth 2.0 that makes it easy to verify a user’s identity and retrieve basic information about the user from the identity provider.

R

Resource Owner

Entity that can grant access to a protected resource. Typically, this is the end-user (User).

Resource Server

Server hosting the protected resources. This is the API you want to access.

T

Tenant

The (mother) organization that a user belongs too.

Subsections of Configure Authway

Modules and Functionality

In this section, a more detailed description of the modules and functions in Authway is provided. We will also attempt to clarify factors to consider when defining the modules and functions needed.

Functions

A function is the smallest component to which permissions can be assigned and, therefore, is the one checked in the code to determine whether the user has the necessary permissions or not. A function should be very stable over time, essentially coming into existence when new functionality is developed and being removed simultaneously when the functionality is removed from the system. The determination of necessary functions is thus guided by the business requirements regarding which parts of the system users need to be able to assign different permissions to.

This also means that a function can vary widely in scope even within the same module/system. For some systems, only a single function representing the entire system is needed, but a more common starting point is to create one function per user interface view or per API. Often, it may be necessary to differentiate between the ability to read information in a view and the ability to manage the information. Examples of functions in Authway include, for instance:

  • Read user
  • Manage user
  • Read groups
  • Manage groups

Sometimes, the act of removal can be more sensitive than adding or modifying information. In such cases, additional functionality may be required, for example:

  • Manage organization (owner)
  • Remove organization (owner)

Within a view, there can also be other types of functions that are more sensitive. An example could be a search function that displays personal information, and if you add the capability to export the search results, it becomes more sensitive as it poses a risk of mishandling personal data. In such a scenario, one might consider having the following functions:

  • Search individuals
  • Export individuals

Another aspect that can impact the definition of functions is whether there are business rules associated with the organization. It is important to avoid moving business rules into Authway, as they belong in the system that has the rule. However, these rules may still need to influence which functions are defined. This is quite common in multi-tenant systems but also in other systems where organizational affiliation can play a significant role. An example could be the ability to administer properties in a real estate company, where a property manager has the right to update information about the properties they are responsible for, but not others. In this case, you might have functions like:

  • Manage my properties
  • Manage all properties

The definition of what “my” means is not specified here, as those rules exist in the business system. During organizational changes, these rules should only need to be updated in the business system, while the functions remain unchanged in Authway (possibly with the exception that you might want to update the description).

Administer functions

When creating functions, you give them a user-friendly name that is displayed in the administration interface, such as in group management. In the “Authorization” field, you specify the string against which the code verifies the permission, and this string must be unique. We recommend using the [Module].[Function] naming convention, for example, Security.ReadUser and Security.ManageUser. It’s also a good practice to create a constant in the code for each permission so that the string is defined in only one place.

A well-written description can help administrators understand the purpose of the function.

Functions that require owner permission must be saved first before adding owners who should have access. This is because the owner is granted access directly but after a check that the function requires owner permission. It also means that when assigning owner permission, you can press “Cancel” to close the dialog, provided no other changes that need saving have been made.

Owner-specific functions

In a multi-tenant system, there is sometimes a need to restrict access to a function to one or more specific owners. This could be for advanced features where there’s a need to view information from other owners, such as in a support organization. It could also involve a customer-specific report that belongs to the Report module.

If there are many functions that need to be specific to owners, it might be advisable to place them in a separate module, as modules are also assigned for each new owner.

The target audience for a function?

Often, the functions that a system contains are available both in a user interface and in an API, meaning that it is reasonable for both users and other systems to have access. However, there are functions that are clearly targeted either at users or systems. An example could be the ability to read an event stream for systems built on events. This is likely to be completely incomprehensible for users and practically never something a user would want to do, but another system is highly likely to need this capability. In Authway, it is possible to restrict where in the administration interface the functions are displayed by selecting a target audience.

Modules

Modules are a grouping of functions that are logically related in some way. A module can correspond to a system, but it is not uncommon to choose to divide a system into several modules. Here are some factors to consider when deciding to create modules:

  • If there are different target audiences (e.g., internal users and external users), it may be a good reason to place functions in different modules.
  • If there are specific owners who should have access to the functions, but there are still quite a few functions that are the same for these different owners, grouping these functions into a separate module may also be a good reason.
  • A module can be set to online/offline during deployments, so it’s often appropriate for modules to match the unit being deployed.
  • An API scope can only correspond to one module, meaning that if you want to require different scopes for calls to different APIs, these probably should be divided into separate modules.

The relation between modules, systems and APIs

Configure an Application

An application (client in OAuth language) that should use Authway to sign-in an end-user or to make system-to-system calls must be configured first. The configuration can be quite daunting, but Authway makes it easy to get started with the most important settings when creating a new application. For now, this guide will only contain the most important aspects of configuring different type of clients and it expects the reader to have read at least the Getting started documentation.

Before configuring a application, you need to decide what kind of application you have created (or shall create). The common ones are:

  • Single Page Application (SPA) - Javascript
  • Web Application - Server side
  • Native Application - Mobile/Desktop
  • Machine/Robot (or system-to-system)

New Client.png New Client.png

All clients must have a client id and we recommend that you decide on a naming convention to follow. We typically use descriptive names with a namespace prefix where each prefix is separated with a dot (".").

Machine/Robot

The Machine/Robot template should be chosen when you have a scenario where a system (client) makes a call to a (resource) server. Authway supports both the classic system-to-system (client credentials) scenario, but also extends that scenario with support for External system. If you want to configure an External system you should read this instead.

For a more classic system-to-system scenario you typically only need to supply a client id and a name. Then on the details of the system you must create a secret (see below about client secrets).

Single Page Application (SPA) - Javascript

Even for a SPA it will be more secure to create a backend for frontend, so that sign-in can be performed as for a Web Application - Server side. The reason for this is that otherwise the access token will reside in the end-users browser and there are no really good way to protect a token in the browser. On the contrary the development around protection of browser cookies have now reached a maturity level that makes them a more secure option and then storing the access token only in the backend. The backend for frontend will typically work as a proxy to the real backend API and exchange the cookie to the access token when passing the call to the API. This will also remove the need to use CORS.

For a SPA you will typically have to configure a client id, client name and URLs for both CORS and redirections. Never create a secret for a SPA client, because if that secret should be used it must be send to the client browser and then it is not a secret anymore.

The CORS URL should be your domain name (incl port if not standard ports are used) without any slash ("/") in the end. The redirect URL must be the exact URL that your client send in the redirect_uri parameter.

On the “Basics” tab you can edit if PKCE is required or not depending on the support in your client implementation.

Web Application - Server side

Web Application should be chosen for all type of web applications that have some kind of backend that can trigger the sign-in flow. This is the most secure type of application for the web. For this scenario you will need to supply a client id, name and redirect URL. The redirect URL must be the exact URL that your client send in the redirect_uri parameter. Then on the details of the system you must create a secret (see below about client secrets).

On the “Basics” tab you can edit if PKCE is required or not depending on the support in your client implementation. If you want to allow the client to request refresh tokens this setting is also configured on the same tab.

Native Application - Mobile/Desktop

Native applications will more or less be configured like a Web Application with client id, name and redirect URL. In this case the redirect URL will not be on the http protocol, but rather something else that you interrupt in the client and continue the flow yourself in the application (instead of in a backend). As always with redirect URL:s it must be the exact URL that your client send in the redirect_uri parameter.

If you have the possibility to protect a client secret in a secure way we recommend that you create a client secret and use it. If you can’t, then there is no meaning in creating a client secret. In the “Basics” tab you can edit if a client secret is required or not.

Client Secrets

A secret can be created and administered on the “Secrets” tab of the client. When a new secret is created, it is displayed directly in the user interface and then it is important that it is copied and stored securely for the client. Authway will not be able to show the value of the secret again (in the same way that passwords cannot be displayed). This means that if the secret is lost, a new secret must be created. When a new secret is created, the old secret should be deleted or given an expiration date. The latter should be used if you want to change your secret in a controlled manner without getting any interruption.

Customize UI for an Application

It is possible to make customization of the login UI per application in Authway. The name of the application is displayed in the text (marked yellow in the image below) and the Logo (marked red in the image below) can be set to any image file that is uploaded to the service.

Client-UI-Customization.png Client-UI-Customization.png

It is also possible to add a custom stylesheet for the application (client), so fonts, colours and other visual aspects can be changed.

Limitations:

  • The stylesheet and image must be uploaded to the Authway service. It is not possible to refer to files on other domains/web servers.

Configure an External System

An external system is a Authway unique concept to make it easier for an API to support both end-users and system-to-system consumption. An external system is both a default configured client, combined with a system user, which makes it possible to get typical user claims like name, email, tid and perm. This possibility makes it easy for the API, since it does not need to figure out if it is an end-user or a system that makes the call and handle them differently. Note that an External System can’t be added to a group so it can’t get any role claims.

It is important to understand that an External System belongs to a tenant, which most likely will have the affect that the API only will return information for that tenant (ie filtered by the tid claim).

Configure an External System

To create a new External System, click add and supply a name and an e-mail address. The same e-mail address can be used for many systems and it will not conflict with regular end-users e-mail address. We do recommend to use a non-personal e-mail because that address might be used by an API owner to inform about downtime, changes and so on.

Once the client has been created, it must have access to at least one functionality in order to be used. At the same time as it receives the right, it will also be assigned the scope that is connected to the module to which the selected functionality belongs.

The last step is then to create a secret.

For an existing client you can’t make many changes, but it is possible to change the e-mail address. The most common administrative task is to make changes to the permissions and in some cases create a new secret.

Secrets for an External System

A secret can be created under the “Actions” menu. When a new secret is created, it is displayed directly in the user interface and then it is important that it is copied and stored securely for the client. Authway will not be able to show the value of the secret again (in the same way that passwords cannot be displayed). This means that if the secret is lost, a new secret must be created. When a new secret is created, the old secret will be valid for 30 days, giving you time to change you configuration.

Advanced Configuration of the Client

It is possible to make changes to the client configuration by going to Configure -> Clients and flip the option to also show External Systems. This will list all External Systems that are configured for the tenant you administer. This makes it possible to change all settings for the client, like the type of tokens used, how long a token should be valid, change allowed scopes and so on.

Configure an API

To be able to protect an API, you need to configure two parts of the Authway service. Step 1 is to define an API scope and with that in place, step 2 is to define an API resource.

Configure an API Scope

A scope defines a logical API where you want to be able to give one or more clients the opportunity to use that API. This means that an application that wants to be able to call the API must request the scope when the token is requested at sign-in. This logical API can have one or more implementations (API resources), but usually a logical API is implemented by an API resource. For Authway, the scope needs to be defined in the same way in both test and production environments.

The naming of a scope should be lowercase (just as the standard identity scopes are) and if you want to distinguish between words, use a “_” between each word. For example Authway API scope is called “idp_auth_api”.

For an API scope, you specify all the claims that the API needs to be able to function, ie what information from the API needs the API to function properly.

There are possible situations for defining several API ranges to one API, such as that one would like to differentiate between a scope that only gives the client access to GET operations (read) and have another scope for the client to use PUT/POST/DELETE operations (updates). Our experience is that it should rarely be controlled per client, but rather by the user’s permissions (ie the user’s access to functionality). In general, we therefore recommend that you use a single API scope for your entire API (GET/PUT/POST/DELETE).

We recommend that an API Scope is associated with a module, so that the administration of External Systems automatically is assigned correct scopes.

Configure an API Resource

An API resource represents an implementation of an API scope. The API resource has a configuration for the site where the API is distributed too, which means that if several distributions of the API is made to different websites (addresses), several API resources should be configured. For the API resource, you specify which API scope the API implements.

We recommend that the API resource is given the same name as the API scope, and if this is done, the API scope is automatically connected to the resource when it is created. This can also be done manually, e.g. if the API implements more than one scope.

Secrets for an API Resource

If the API resource should be able to use OAuth/OpenId Connect APIs that are protected, a secret is required (corresponding to a user’s password). Examples of such use cases are is to use introspection to support reference tokens along with access tokens.

This secret can be created and administered on the “Secrets” tab of the API resource. When a new secret is created, it is displayed directly in the user interface and then it is important that it is copied and stored securely for the API. Authway will not be able to show the value of the secret again (in the same way that passwords cannot be displayed). This means that if the secret is lost, a new secret must be created. When a new secret is created, the old secret should be deleted or given an expiration date. The latter should be used if you want to change your secret in a controlled manner without getting any interruption.

Configure a Tenant

Every user belongs to a single organization, but a human being is allowed to be associated with multiple tenants by having a user account in each tenant. The user account is allowed to have the exact same credentials (for example the exact same username and password). To be able to add users, there must exist an organization to add them too. There is one build-in organization for private individuals, but for all customers, suppliers and partners that you want to allow access for you must create a tenant.

Considerations for Creating Tenants and Organizations

These are the possibilities and limitations that you should have in mind when deciding how you should add an organization to Authway:

  • An user can belong to more than one tenant, ie the same username (and other credentials) can be used in multiple tenants. If the user uses exactly the same credentials, Authway will ask the user for which tenant he/she wants to sign-in. The user can only be signed in to one tenant at a time.
  • An user can only belong to one organization in the organization tree.
  • Modules can only be activated/deactivated for a tenant and not for any subsidiaries (and the same rule applies to functionalities that requires tenant permission). If you want different parts of the organization to have access to different modules, this indicates that you should configure them as different owners. At the same time, this can to some extent also be handled by creating different groups for different parts of the organization that are only assigned rights to the modules they are supposed to use, but if a strict separation is required where groups cannot be configured incorrectly, different tenants must be set up.
  • Trusted domains are required to be unique per tenant, which will affect the possibility to configure auto-provision.
  • It is possible to configure the exactly same external identity provider on multiple tenants, but in many cases the need to do this is an indication that the organization maybe should be a single tenant.
  • An application (client) will always get the tid (tenant id) claim for a user, but it is also possible to get more organization information like the organization id a user belongs to.

Create a Tenant

You create new tenants under the Configure -> Tenant menu and the only required information is the name of an organization. The name must be unique over all tenants. It is possible to optionally add an organization identity and after creating the tenant you can add subsidiaries to the tenant.

Manage a Tenant

When the organizational structure of a tenant is defined there are mainly two things you want to manage:

  • Modules: activate modules that users of the tenant should be allowed to access (authorization).
  • Settings: make custom settings for authentication of users belonging to the tenant. This is done by editing, disabling och adding identity providers for the tenant.

To be able to make any changes of the identity providers for a tenant, you must first assign a unique URL name for the organization. This can often be a shorter version of the name, for example we use “priv” for the tenant “Private individuals”. The reason for this setting to be required is that it will be part of the URL:s when customizing identity providers.

Configuring Identity Providers

All configured identity providers for the service is visible in a list under settings. For each of the listed identity providers you can enable/disable the usage of the identity provider for this specific tenant. Depending on the specific identity provider more customization can be done.

A common configuration to do is to configure the Microsoft identity provider to only allow users from the organization’s Azure AD Tenant. To do this, select the Microsoft identity provider and click Edit. Paste the tenant id from Azure AD and optionally change the name, probably to something like the organization name (important that the user will understand that they will be able to use their existing Azure AD identity).

It is also possible to configure two levels of auto-provisioning of users for some of the identity providers. Check that the identity provider is allowed to automatically create users. By doing so you are enabling the first level that we call auto-link. This level still requires an administrator to manually add the user to the service, but it is not needed to send an invitation. Instead the user will be re-directed to the external identity provider when trying to sign-in and when returning the user will be auto-linked based on the username being the same in Authway and the external identity provider.

Authway can also create the user completely at sign-in and therefor removing all manual administration of users in Authway. To enable this, one or more trusted domains must be added in addition to allow the users to be automatically created. The sign-in process will match everything after “@” in the e-mail address (username) to see if there is an organization with auto-provision configured. If a match is found, the user is re-directed to the external identity provider and when returning the user is automatically created. If any piece of information is missing when creating the user, the user will be prompted for that information the first time, but not after the user creation is finished.

Delete a Tenant

It is always sad to see a customer, supplier or partner to leave, but when it happens it is important to remove the information from the system to be compliant with regulations such as GDPR. To delete a tenant you search for it, and click on the name to get to the details page. Under the Actions menu you’ll find the delete command if you have the necessary permission. Remember that deleting a tenant is an action that can not be undone.

Access Control With Modules

Access control with modules empowers you to manage and restrict user access at a tenant level for an application. By activating a module with a tenant, Authway enforce the requirement that users must be part of a tenant with the specific module activated in order to sign in to an application.

Here is how it works:

  1. Module set up: Start by defining a module that is a logic representation of an application. For the purpose of controlling access to the module on a tenant level, it is not necessary to configure any permissions and/or role templates.
  2. Module integration: Next, connect the module to an application in through application configuration. When you do this, Authway will automatically enforce access restrictions. Only users who belong to a tenant with the module enabled will be allowed to sign in. Users not meeting this criteria will be prevented from accessing the application.
  3. Tenant-Level Control: Configure which tenants should have access to the module, by activating the module for tenants that are allowed to use the application. Read more in “Configure a tenant”.
  4. User Experience: If a user is signed in but doesn’t have access to the required module, Authway will effectively log them out, preventing unauthorized access and maintaining the integrity of your access control system. In the same time it also allows the user to sign-in with other credentials that might have access to the application. For users that exists in multiple organisations, Authway will try to automatically choose organisation depending on access to the module, during sign-in.

Subsections of User Management

Create and Invite Users

Auto provisioning of Users

Auto provisioning is automated management of all users for an organisation. Authway supports auto provisiong of users, which allows a smoother user experience, better efficiency and improved security.

Configure Auto provision for an organisation (tenant)?

Search for the organisation (or choose My organisation) and switch to the Settings tab. Choose the sign-in alternative that should be enabled for auto-provision or configure a new tenant specific sign-in alternative. Register one or more trusted e-mail domains (Authway consider everything after @ as the e-mail domain).

Configure Groups

All groups in Authway can be set as External group. The group is made external by enabling that and enter the name or unique identifier of the group from the home directory. It will not be possible to manually add or remove users from an external group, instead a user will be added/removed to the group during sign-in depending on if the group is recieved from the home directory or not.

Sample form to get required information from customers

At [YOUR ORGANISATION], we want to give you the best possible service and the smoothest possible experience with our digital services. We therefore recommend that you use the logins that your employees already have in your organisation, so-called single-sign-on (SSO). It is both smoother and more secure.

  1. How do your users log in to your own IT environment?

    [ ] Microsoft Entra ID (e.g. if you use Microsoft 365 (Office))

    [ ] Local AD or other alternative

    [ ] We do not want to use existing logins (SSO)

If you have answered “Local AD or other alternative” above, our respective IT departments need to talk to each other to see if it can be resolved. If you have answered “Microsoft Entra ID” above, you (or your IT department) can answer the following questions. For each question you answer, your administration is simplified.

  1. Enter your Microsoft Entra ID:

    By entering your Entra ID, users will be able to log in with their existing accounts in your organization (SSO).

  2. Enter the Object ID for one or more security groups that you want to map to the following roles in [YOUR SERVICE]:

    [GROUP 1]:

    [GROUP 2]:

    [GROUP 3]:

    [GROUP 4]:

    [GROUP 5]:

    By doing so, your users will automatically receive the correct permissions in FleetWeb based on which groups they belong to in Entra. Please note that you or we still need to control which companies and cost centers users with higher permissions than Driver will have access to. FleetManager must also be approved by the company signatory.

  3. Enter the email domains (the entire part after @, e.g. authway.co) that you want users to be able to automatically log in from:

    By entering email domains, your users can automatically log in without the need to administer them in our services.

Verify User Emails

Authway uses e-mail addresses as the default username and to ensure that the user can have control over their account it is important to verify that the user is in control of it. We recommend that e-mail verification is required and this is configured for the service by IRM (during initial configuration or added later).

The verification is done in one of two ways:

  1. If the user is invited, the invitation is send to the user e-mail with a magic link. When the user clicks the link to create their account, the e-mail address is verified by the magic link. The magic link is by default valid for 3 days.
  2. If a verified e-mail is required (by the service configuration) and the user email is unverified, the user will be required to verify the address during sign-in. This is done by sending a time-based one-time password (TOTP) containing six numbers to their e-mail address. When the user enters the correct code, the e-mail address is verified and the user can continue. The code is is valid for about 15 minutes.

The second option is very common after migrating users or when the requirement to verify e-mail address is turned on after the service have been used for a while. Invited users will always get their e-mail verified through the invitation, even if it is not required by the service.

It is also possible that the e-mail address is automatically verified by a trusted external sign-in, such as Microsoft Entra ID (Azure AD).

Requirement to verify the e-mail address can also be configured differently per organisation (tenant).

Delete Users

Subsections of Developer guide

Create a User

An administrator can create users in Authway admin UI and we always recommend to evaluate if auto-provisioning is possible since that remove a lot of manual (and double) administration. There are also valid reasons to use our admin APIs to create users.

A user in Authway is actually two objects, a person object and a user object. A person can exists without a user, but a user always requires a person. Both objects exists in a tenant and a person can be assigned to a subsidary if needed. There are scenarios where only a person should be created, but focus will be on users in this document.

Since the person/user is bound to a specific tenant all API calls to them is required to include tenant information as explained in the Introduction to admin APIs.

API:s to create a person and a user

Since a user requires a person, the normal way of creating a user is through the POST Person API. In the post it is possible to pass parameters so that both the person and the user is created in a single call. It is not possible to set password or other credentials for a user, but instead a user should be invited (or auto-linking can be used).

Please don’t pass the Id (it is not required) if not absolutely necessary as explained in the “Introduction to admin APIs”.

Invite a user

When creating a user through the POST Person API, there are a flag that can be set if you want Authway to send an invitation to the user. When setting the flag to true, it is also possible to pass additional parameters that should be added to the invitation link. Additional parameters:

  • clientId=[a client id]: The value of a registered application (client). When this parameter is passed the pages will use custom styles if that is registered for the client. The user will also be returned to URL registered on the client, when the account creation is completed.
  • link=true: Triggers the flow where a invited user will have to sign-in or create a personal account that the organisation account will be linked to. Read more about linking of users.

It is also possible to get an invitation link through the API if you want to share the link through other mechanisms or if more control over the e-mail creation is needed.

An invitation link is valid for 24 hours by default, but this can be configured in your instance.

Events during creation of person and user

There will be multiple events during the process of creating a user.

During the API calls to POST Person:

When the user uses the invitation link and completes the registration:

If a clientId parameter is included in the invitation link, the events will have metadata about the client (ClientId and ClientName); otherwise not.

Assign groups to a user

Normally it is also required to assign groups to the user so that the user will have permissions to do anything in the applications. Groups are assigned through the Group API. When adding groups it is possible to pass group id:s or group names, but it is not allowed to send a mix of both.

Subsections of Create a User

Link Users

Read about linked users so that you understand the concept and its limitations before continuing on this more developer focused documentation.

Create linked users

By invitation

Add query string parameter link=true to the invitation link will trigger a flow where the user must sign-in with (or create) a personal account which will do the linking automatically.

By API calls

To create users that should be linked you do the same as for a regular user. After the user is created, it is possible to link the user to a parent by POST users/{newly created user id}/link/{parent user id}.

Events during linking

A UserClaimAdded event is raised when a user is linked to a parent user. The event is raised to the child user (the same user that the link API was used for), which means that the AggregateId will have the value of the child user. The ClaimType will be “sub” and the ClaimValue will have the unique identifier of the parent user.

User sign-in

Authentication of the user must be performed by the Identity Provider (IdP) (in this case by Authway), where the user´s session or credentials will be checked. It is an application that wants to authenticate the user, that triggers the sign-in flow by using either the OpenId Connect protocol (OIDC) (preferred) or the SAML protocol (only available in Enterprise Edition and less preferred, but sometimes necessary). The application is called Client in OIDC and Service Provider (SP) in SAML. The authentication is done by a web browser, running on the user´s device, by redirecting the user to the IdP. How the users are authenticated is left up to the IdP to decide (even if the application can pass parameters affecting how that decision is done).

Considerations for different kind of applications

A server-based web application can choose to use OIDC or SAML.

Mobile applications running on platforms such as iOS and/or Android should use the system browser to re-direct the user for authentication. Embedded web views are not trusted, since there is nothing that prevents the app from snooping on the user password. User authentication must always be done in a trusted context that is separate from the app.

Single Page Applications (SPA) or other browser (JavaScript) applications should carefully choose how to handle authentication and we have a separate page with more details about this.

Choose OpenId Connect Flow

OIDC has flows designed for the different application types.

  • Authorization code flow: This is the most used and the preferred flow, primarly used by server-base applications and native/mobile apps. Authentication is triggered by re-directing the user to the IdP, but when the user is returned only a code is passed back to the application through the user browser, never any token(s). Instead, the tokens are fetched by using a back-channel where the code is exchanged for the token(s). This is the most secure flow since the tokens are never exposed to the browser and it also makes it easy to authenticate the application (client). If possible, it also good to use this flow with PKCE.
  • Implicit flow: Can be used by SPA or other JavaScript applications that don’t have a backend. Token(s) are send directly to the browser during re-direction of the user after a successful sign-in. Therefor this flow is less secure and as detailed in our article you should consider to use a Backend for Frontend (BFF).
  • Hybrid flow: Rarely used nowdays and it is a combination of implicit and code flow.

SAML Authentication

Authway supports SAML authentication in the Enterprise edition, but is must be actively turned on by IRM. Authway supports both Redirect and Post Bindings.

Subsections of User sign-in

Choose Authentication for a SPA

When building a SPA there are two alternatives (both uses OpenId Connect protocol) for how to authenticate the users. Traditionally the authentication have been triggered by the end users browser, which means that the tokens are returned to the end users browser and then the token(s) are typically stored on the client. The token are then used when making API calls to the backend.

SPA-triggered-authentication.png SPA-triggered-authentication.png

There are a couple of challenges with this and the biggest one is that there are no real secure way to protected the token in the end users browser (resulting in a larger attack vector). A smaller problem is that the client can’t use a secret against the Identity Provider (if a secret is required, it must be send to the browser and in that case it is not secret anymore).

During the last couple of years the protection and restrictions around cookies have improved a lot and a cookie is therefor a much safer way to handle an authenticated user today. This has evolved a pattern called Backend-for-frontend which makes it possible to use cookies in the end users browser instead of tokens. The backend-for-frontend is just a proxy to the backend API (for example implemented by using YARP, Ocelot or another solution) that identifies the user through the cookie and then exchange the cookie to the corresponding token that is stored on the sever side and finally passes the token to the backend API.

BFF-triggered-authentication.png BFF-triggered-authentication.png

The result of this is that the token is securely stored on the server. To use this pattern, you will trigger the sign-in from the server (as in a traditional web application) instead of doing it from the client, for example before the SPA application is started. After the sign-in is completed the user is redirected (back) to the SPA client.

This is not just a more secure solution, but it is also easier in that it removes all CORS problems and it is typically easier to use refresh tokens on the server side than on the client.

OpenId Connect Authentication with Authorisation Code Flow

We strongly recommend the use of standardized and validated client libraries from you application code. Security is very difficult to get correct and this article will just show a simplified minimal implementation of the authentication of a user with OpenId Connect (OIDC), more in the purpose to educate the basics than showing production ready code.

Initiating user authentication

The application will initiate a user authentication by re-directing the browser to the authorization endpoint of the Identity Provider (IdP, in this case Authway). The re-direct must be an OIDC authentication request message. Here is an example of the re-direct by the application:

HTTP/1.1 302 Found
Location: https://YOURINSTANCE.irmciam.se/connect/authorize?
          client_id=YOUR_CLIENT_ID
          &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcallback
          &response_type=code
          &scope=openid
          &state=YOUR_STATE

Explanation of the request parameters in the example:

  • client_id: The application (Client) identifier in Authway. This identifier is created when the application is registered in Authway.
  • redirect_uri: The callback URI where the application want to retrieve the authentication response. This URI is validated by Authway and it must be an exact match of a registered redirect URI.
  • response_type: Set to code to indicate that it is the authorisation code flow that should be used.
  • scope: Used to specify the requested authorisation in OAuth. openid indicates a request for OIDC authentication and a ID token. More scopes can be supplied to indicate what information should be included in the ID token.
  • state: Value set by the application to keep state between the request and the callback.

There are more parameters that can be used.

At the Idp the user will be prompted to sign-in, if that is not already the case. After the Idp has successfully authenticated the user it will call the application redirect_uri with an authorization code (or an error code if Idp fails).

HTTP/1.1 302 Found
Location: https://client.example.org/callback?
          code=A_CODE_ISSUED_BY_IDP
          &state=YOUR_STATE

The application must validate the state parameter and if successful continue to exchange the code for the ID token.

Exchange code for ID token

The retrieved code is meaningless to the application and must be exchanged for an ID token by making a back-channel call to the Idp. By making this in a back-channel the client can be validated, so that the Idp is only revealing tokens to trusted applications, and the ID token is never exposed to the browser (which is more secure).

The code can be exchanged at the token endpoint:

POST /connect/token HTTP/1.1
Host: YOURINSTANCE.irmciam.se
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3

grant_type=authorization_code
 &code=A_CODE_ISSUED_BY_IDP
 &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcallback

Client ID and secret can be passed in the Authorization header or as client_id and client_secret parameters in the request body. The form-encoded parameters should include a repeated redirect_uri and the retreved code from the response after the successful authentication of the user.

If the Idp handles the request successfully it will return a JSON object with the ID token, an access token and optionally a refresh token together with expiration information.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
  "id_token": "A_JWT_TOKEN",
  "access_token": "AN_ACCESS_TOKEN",
  "token_type": "Bearer",
  "expires_in": 3600,
}

The ID token should be validated by the application before it is accepted.

Authorization Endpoint Parameters

Request parameters

Parameter Required Description
acr_values no Space seperated string with special requests from the client. See below.
client_id yes The unique id of the requesting client.
code_challange no, but required for client configured for PKCE.
code_challenge_method
login_hint no Hint of the username. Could be used if the client asks for username before redirecting.
max_age no Specifies how much time (in seconds) that is allowed to been passed since user singed in. See Force re-authentication of user.
nonce no A random string value used to mitigate replay attacks by associating the client session with the ID token.
prompt no “none”, “login”, “consent”, “select_account” or “create”. Indicates how the client wants that Authway handles the request.
redirect_uri yes The callback URL the client wants to re-direct to.
request no
request_uri no
response_mode
response_type yes
scope yes A space separated string of scopes that the client wants access to.
sso_token no A non-standardized parameters to enable some extra single-sign-on scenarios.
state no A random value that will be passed back to the client. Can be used to keep track of a session or to prevent unsolicited flows.
ui_locales no End-User’s preferred languages, represented as a space-separated list of language tag values, ordered by preference. For instance, the value “sv-SE en” represents a preference for Swedish as spoken in Sweden, then English (without a region designation).

acr_values parameters

The acr_values parameters are passed as “parameter:value” and if multiple parameters are passed they should be seperated with a space. For example:

tenant:priv idp:bankid
Parameter Description
idp The unique identifier of the sign-in method to use. See Control authentication method from the client.
tenant The unique identifier of an owner (tenant) that the user must belong to. See Only allow users from specific tenant
impersonate Trigger the flow to impersonate another user. See Impersonate a user.

Default Claims Supported

This is the default claims supported by Authway, but custom support can be added for more/other claims in a customer instance. The scope that the claims belong to is also the default, but could differ for a specific customer.

Scope Claim Description
openid sub A unique value identifying the user.
openid tid A unique value identifying the tenant the user belongs to.
profile family_name The surname of the user. For example “Doe”.
profile given_name The given name of the user. For example “Joe”.
profile name The full name of the user (given_name + ’ ’ + family_name). For example “Joe Doe”.
profile preferred_username The user name used during sign-in, most commonly the e-mail address.
profile picture A URL with a user picture taken from Gravatar or the initials if the user does not have a Gravatar picture.
email email The email address of the user. Could exists twice if the user has another e-mail registered on the person.
email email_verified True if the email address have been verified and it is always the same e-mail address used as user name that are confirmed (or not).
phone phone_number The phone number of the user.
phone phone_number_verified True if the phone number have been verified; otherwise False.
org orgid The unique identifier for the organisation the user belongs to. Will be the same as tid for users belonging to the mother organisation, but could be different for users belonging to a subsidiary.
org orgin The organisation number identifying the organisation, if the value exists.
org company_name The name of the organisation.
roles role The name of the group the user belongs to. Zero, one or more claims of this type could exists depending on the number of groups the user belongs to.
perms perm The unique identifier of a permission that the user have. Zero, one or more claims of this type could exists depending on the number of permissions the user got.

Protocol claims

Claim Description
amr The authentication used when the user signed in. The value would be pwd for password or external for an external identity provider (idp). More than one amr claim can exists. For example if multi-factor authentication is used there will be an amr claim with the mfa value.
idp The identity provider used when amr is external or local if the user used a password to sign-in.
auth_time The time the user authenticated. Could be used to evaluate if a re-authentication is required.

There are more protocol claims, but those are the most commonly used claims by developers themselves.

Special claims

Impersonated users

If a user is impersonated there will be an act claim with a serialized json structure with standard claims from the original user performing the impersonation. For example:

"act": {
   "oid":"d5542f98-8a6f-6d2a-cda0-39fc52ae2b58",
   "sub":"295A0000-E969-E6E6-3826-08DB0DD1E036",
   "tid":"a27446b6-795e-4ccc-1da6-39fc52ae2b37"
}

If impersonation is used there will also be a second amr claim with the value imp (for impersonate).

Linked accounts

If linked account are used the sub claim will have the same value for all linked accounts (that is the definition of linked accounts) which means that it is not unique over all tenants anymore. In that case a oid claim (object id) is also issued with a true unique identifier over all tenants.

For linked accounts the a may_login claim is also issued for org scope, which contains a serialized JSON array with information about other organisations that the user could switch to. For example:

"may_login": {
   [{
      "oid":"d5542f98-8a6f-6d2a-cda0-39fc52ae2b58", 
      "tid":"ffffffff-ffff-ffff-ffff-ffffffffffff",
      "companyname": "Privatpersoner"
   }]
}

OpenId Connect Authentication with .NET

To log in a user according to this guide, you must have configured an application (client) as a web application (server).

To log in a user, you use the OpenID Connect support available in .NET. This guide will show how it’s done in .NET 5, but for the most part, the process is similar regardless of the framework version.

Add the NuGet package for OpenID Connect:

<ItemGroup>   
  <PackageReference Include="IdentityModel" Version="5.0.1" />
  <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.3" />
</ItemGroup>

Here is also support for IdentityModel, which has several useful classes when working with OpenID Connect and claims.

Configure OpenID Connect and Cookies so that the application uses the IdP to identify users (in Startup.ConfigureServices):

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "https://environment-company.irmciam.se/";

    options.ClientId = "Company.ApplicationId";
    options.ClientSecret = "supertopsecret";
    options.ResponseType = "code";
    options.SaveTokens = true;
    options.MapInboundClaims = false;
});

In the project template, authentication is not enabled by default, a line must also be added in Startup.Configure (before UseAuthorization):

app.UseAuthentication();
app.UseAuthorization();

To trigger the login, there needs to be one or more protected pages. This can be configured in many different ways and is well-documented by Microsoft. For this example, we make a simple modification to the HomeController by adding the Authorize attribute, which forces the user to log in immediately.

[Authorize]
public class HomeController : Controller

Now you can test the application, and assuming everything is configured in the IdP, you should land directly on the IdP’s login page. After the login is completed, you will be redirected back to the application.

With the above configuration, User.Identity.Name may not display anything. This is because, for compatibility reasons, Microsoft reads the wrong claim, and the code must specify which claim to use instead. Make the following change to the options for the OpenID Connect configuration (which also sets the correct role claim):

...
options.SaveTokens = true;

options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
    NameClaimType = JwtClaimTypes.Name, //JwtClaimTypes.PreferredUserName, eller vad du önskar
    RoleClaimType = JwtClaimTypes.Role
};

JwtClaimTypes is part of the IdentityModel package that we added to the project file in the first step above.

To see which claims and other relevant information are available, you can add the following to a Razor view:

    <h2>Claims</h2>

    <dl>
        @foreach (var claim in User.Claims)
        {
            <dt>@claim.Type</dt>
            <dd>@claim.Value</dd>
        }
    </dl>

    <h2>Properties</h2>

    <dl>
        @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
        {
            <dt>@prop.Key</dt>
            <dd>@prop.Value</dd>
        }
    </dl>

What you quickly notice on a page displaying the user’s claims is that there are only a few claims compared to those available in the IdP. To get more claims, additional configuration is needed. All this configuration is also done in the AddOpenIdConnect call from above.

To retrieve more claims, you need to specify that more claims should be fetched from the UserInfo endpoint:

options.GetClaimsFromUserInfoEndpoint = true;

In OpenID Connect, the client (the application) requests which scopes it wants. Each scope is associated with one or more claims (this configuration is in the IdP). Common standard scopes include:

  • openid (must be requested).
  • profile (contains profile information for the user, such as name, picture, web pages, etc.).
  • email (contains email and whether the email address is verified).
  • phone (contains phone number and whether the number is verified, but this is not used by the IdP today).
  • address (contains address information, but is not used by the IdP today).
  • org (contains organizational information for users belonging to an organization; this is a custom scope for the IdP).

Microsoft’s default configuration of OpenID Connect automatically adds openid and profile, but if you want to include the user’s email, you need to add that scope to the configuration:

//Begär de scopes (gruppering av claims) som önskas
//options.Scope.Add("openid"); //Default by Microsoft
//options.Scope.Add("profile"); //Default by Microsoft
options.Scope.Add("email");
options.Scope.Add("org"); //IRM Idp organisationsinformation

If you run the application with these settings, you will unfortunately still notice that several claims are missing. This is because Microsoft has a concept called ClaimActions, which means that only the claims for which there is a ClaimAction will be transferred to the ClaimsIdentity during login. Therefore, we need to add more actions:

options.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Picture, JwtClaimTypes.Picture);
options.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.EmailVerified, JwtClaimTypes.EmailVerified);
options.ClaimActions.MapUniqueJsonKey(Constants.JwtClaimTypes.TenantId, Constants.JwtClaimTypes.TenantId); //IRM IdP tenant id
options.ClaimActions.MapUniqueJsonKey(Constants.JwtClaimTypes.OrganisationId, Constants.JwtClaimTypes.OrganisationId); // IRM IdP organization id (same as before if the person belongs to the parent company, but may be different)
options.ClaimActions.MapUniqueJsonKey(Constants.JwtClaimTypes.CompanyName, Constants.JwtClaimTypes.CompanyName);

Several of these claims are unique to the IdP, and for this, we have created our own Constants class in the project (also declared in IRM.dll):

public static class Constants
{
    public static class JwtClaimTypes
    {
        public static string TenantId = "tid";
        public static string OrganisationId = "orgin";
        public static string CompanyName = "companyname";
        public static string Permission = "perm";
    }
}

With this configuration, you will see many of the claims that the IdP can provide about a user. In IRM.AspNetCpre.Mvc, there is a convenient extension method to add scopes and typical claim actions so that the application can access the claims included in each scope:

options.AddScope(StandardScopes.OpenId); //Default added by Microsoft, but here, a mapping is also done for OwnerId/TenantId
options.AddScope(StandardScopes.Profile); //Default added by Microsoft, but here, mappings are also done for, for example, picture.
options.AddScope(StandardScopes.Email);
options.AddScope(CustomScopes.Roles);
options.AddScope(CustomScopes.Permission);
options.AddScope(CustomScopes.Organisation);

Since we have specified SaveTokens = true above, we can retrieve the access token to make calls to an API:

var accessToken = await HttpContext.GetTokenAsync("access_token");

Note that if you retrieve many scopes and map many claims, it can result in a large number of claims, leading to a large cookie. In general, it’s good to keep the cookie size down, so this is something to consider. In AddCookie, there is an option to configure a SessionStore, allowing you to choose to store the content on the server instead, for example, in a distributed cache.

In IRM.AspNetCore.Mvc, there is a SessionStore that uses MemoryCache and/or DistributedCache if configured. Just call AddCacheSessionStore after AddCookie:

.AddCookie("Cookies", options =>
{
    options.Cookie.HttpOnly = true;
    options.Cookie.SameSite = SameSiteMode.Lax;
    options.Cookie.IsEssential = true;
}).AddCacheSessionStore()

Protect user information from the IdP

There are several challenges when requesting many scopes when a user (or system) is logging in:

  • The ticket can easily become a bit large, or especially the cookie can become large. It might sound strange, but it’s important to consider that when the cookie is saved, the user’s claims are stored, and often id_token, access_token, and possibly a refresh_token are also stored. This means that each claim can potentially exist two to three times in the cookie, and the size can quickly grow.
  • The ticket can contain sensitive information, such as personal information obtained when requesting profile, email, and similar scopes. Where will the ticket be stored? If it is to be sent to a user’s device to be included in API calls, there are not many great ways to protect the ticket and thus the sensitive information.

What can be done to reduce the size and increase security?

Avoid, if possible, sending any token to the client

Since there are no really good ways to securely protect a token on the client, the safest method is not to send any token to the client at all. Today, the recommended approach is to use cookies from the client because cookies can be securely managed (set them to HttpOnly and Strict) in an effective manner.

The challenge with cookies arises when making calls to an API with a different address than the website because the cookie is only valid on the website itself. If the client needs to make requests to an API at a different address, there are two alternatives:

  1. Send the token to the client, but preferably use a reference token. However, try to avoid this option if possible.
  2. Transform your web solution into an API gateway for the underlying API. Ensure that all API calls are routed back to the website, acting as an API gateway. For calls that need to proceed to an underlying API, forward them accordingly. When forwarding the request, append the access token associated with the logged-in user. It is not necessary to write many lines of code for this if you use a tool like YARP or Ocelot (you can also read more here).

Use a reference token instead of a JWT for your access token

An access_token can be of two types: 1) a JWT or 2) a reference token. The type an application receives is configured in the Identity Provider (IdP). A JWT contains all claims and some additional information, while a reference token is just a reference to the actual ticket. However, the real ticket must be fetched by the API from the IdP. The advantage of a reference token is that it is very small, and even if someone obtains the ticket, it contains no information by itself. The drawback of reference tokens is that the API must be able to handle this type of token, and typically, one would want the API to handle both JWT and reference tokens. This is well-documented, and there is some existing code that already supports it (you can start reading after the first bullet list, which is roughly in the middle of the blog post).

Request fewer scopes

Never request more scopes than absolutely necessary is a good general rule, but there is support in the Identity Provider (IdP) to complement with additional scopes from the server/API afterward. Besides the advantage of reducing the ticket size and containing less sensitive information, it also means that each application doesn’t need to know in advance which scopes each API it calls requires. The most crucial aspect is perhaps that it allows an API to evolve over time without requiring changes to calling applications.

Therefore, we recommend only fetching openid, the API scopes for the APIs to be called, and possibly the offline scope if a refresh token is needed. If you choose to proceed this way, both the web application and the API will need logic to fetch additional scopes. Here is example code for retrieving claims for additional scopes:

public Task<List<Claim>> GetAuthorizationAsync(ClaimsPrincipal user, List<string> scopes)
{
    if (!user.Identity.IsAuthenticated)
        return Task.FromResult(new List<Claim>());

    const string formEncoded = "application/x-www-form-urlencoded";

    var userId = user.FindFirst("sub")?.Value;
    if (string.IsNullOrEmpty(userId)) //Ingen autentierad användare, åtminstone inte någon som vi kan hämta behörigheter, profile eller något annat för
        return Task.FromResult(new List<Claim>());

    var cacheKey = CreateCacheKey(userId, scopes);

    return _cache.GetOrCreateAsync(cacheKey, async entry =>
    {
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(_options.CacheLength);

        var clientId = _options.ApiName;
        var clientSecret = _options.ApiSecret;

        var arguments = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("client_id", clientId),
            new KeyValuePair<string, string>("client_secret", clientSecret),
            new KeyValuePair<string, string>("user_id", userId),
            new KeyValuePair<string, string>("scopes", string.Join(" ", scopes))
        };

        using (var content = new FormUrlEncodedContent(arguments))
        using (var client = new HttpClient())
        {
            content.Headers.ContentType = new MediaTypeHeaderValue(formEncoded) { CharSet = "UTF-8" };
            client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", formEncoded);

            using (var response = await client.PostAsync(_baseUri + "api/client/authorization", content).ConfigureAwait(false))
            {
                response.EnsureSuccessStatusCode();

                using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                using (var reader = new StreamReader(stream))
                {
                    stream.Position = 0;

                    var serverClaims = Serializer.Deserialize<List<Claim>>(new JsonTextReader(reader));
                    return serverClaims;
                }
            }

        }
    });

}

ApiName and ApiSecret are obtained when an API is configured in the Identity Provider (IdP). If it’s a web application, ClientId and ClientSecret can be sent instead. In the code above, _baseUri needs to be set to the URL of the Auth server.

This function can then be called from, for example, a ClaimsTransformation class.

This logic is also necessary when using the Client Credentials flow (External systems) to make calls to your APIs. Fundamentally, a ticket for a Client cannot contain any personal claims since it represents a system/application. However, the IdP has specific support called External systems, allowing an API not to distinguish between a user or a calling system, simplifying many aspects.

In IRM.AspNetCore.Authorization.Client, there is ready-to-use support that is easy to configure, achieving exactly this.

In the call to AddCookie, there is the possibility to configure a SessionStore that allows saving the content of the cookie on the server instead, making the cookie itself just a reference. Here’s an example using IMemoryCache, but it can certainly be switched to IDistributedCache or some other type of storage.

public class MemoryCacheTicketStore : ITicketStore
{
    private readonly IMemoryCache _cache;
    private const string CacheKeyPrefix = "MemoryCacheTicketStore-";

    public MemoryCacheTicketStore(IMemoryCache cache)
    {
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
    }

    public async Task<string> StoreAsync(AuthenticationTicket ticket)
    {
        string key = await _backingTicketStore.StoreAsync(ticket);
        AddToCache(key, ticket);

        return key;
    }

    public async Task RenewAsync(string key, AuthenticationTicket ticket)
    {
        AddToCache(key, ticket);
    }

    private void AddToCache(string key, AuthenticationTicket ticket)
    {
        var options = new MemoryCacheEntryOptions
        {
            Priority = CacheItemPriority.NeverRemove
        };
        var expiresUtc = ticket.Properties.ExpiresUtc ?? DateTimeOffset.UtcNow.AddMinutes(20);
        options.SetAbsoluteExpiration(expiresUtc);

        _cache.Set(key, ticket, options);
    }

    public async Task<AuthenticationTicket> RetrieveAsync(string key)
    {
        _cache.TryGetValue(key, out AuthenticationTicket ticket);
        return ticket;
    }

    public async Task RemoveAsync(string key)
    {
        _cache.Remove(key);
    }
}

In IRM.AspNetCore.Authentication, there is ready-made support for using the cache as a session store:

AddCookie("Cookies", options =>
    {
        options.Cookie.HttpOnly = true;
        options.Cookie.SameSite = SameSiteMode.Strict;
        options.Cookie.IsEssential = true;
    }).AddCacheSessionStore();

Here, a primarily distributed cache will be used if configured, but the support falls back to MemoryCache if it is not available.

Only allow users from a specific tenant to sign in

In some situations, you might want to control that only users that belong to a specific tenant (organisation) are allowed to sign in. It can e.g. be that a module/system is only used by internal users or only private individuals, etc.

There are two possible solutions that can be used:

  1. Connect the application to a module in configuration. The modules are enabled for the organisations (tenants) that should be able to use them. If a user belonging to an organisation that does not have access to the module tries to access it, the sign-in will be prevented and the user informed that they don’t have access. This can be used for applications that uses OpenId Connect or SAML protocol.
  2. In code it is possible by sending “tenant:GUID” (or “tenant:shortname”) in OpenId Connect parameter acr_values.

C# example

.AddOpenIdConnect(options =>
{
    //Other configuration not shown
    options.Events.OnRedirectToIdentityProvider = ctx =>
    {
        ctx.ProtocolMessage.AcrValues = "tenant:" + tenantId;

        return Task.CompletedTask;
    };
});

Control authentication method from the Client (application)

In some situations an application want to control how the user should be authenticated, for example require the user to use BankId to sign-in. This can be done in two ways:

  1. By configuration. Configure the client to only allow one authentication method and this will be the only choice possible for the user. This option is easiest if it should always be the same for all users. This option is supported for both OpenId Connect and SAML applications.
  2. Pass authentication method as argument in OpenId Connect acr_Values parameter. This will allow the application to pass different values for different users/situations.
  3. It is possible to pass arguments from a SAML application that requires or disables local sign-in. This is done by passing Password Protected Transport (urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport) as requested authn context together with Exact as comparison to require local sign-in, or Better as comparison to disable local sign-in.

Pass authentication method in acr_values

To pass authentication method you should use “idp:schemename” where schemename is the authentication method, for example local (to only use local passwords) or bankid to use Swedish BankId (for instance where this is configured). Valid values is per tenant.

BankId

As stated above the application can pass bankid as a value for idp. This will result in a prompt where the user will have to choose to use BankId on same device or on another device. It is also possible for the application to pass bankid-samedevice to shortcut the prompt and take the user immediately to sign-in on same device; or bankid-otherdevice to use another device to sign-in.

C# example

.AddOpenIdConnect(options =>
{
    //Other configuration not shown
    options.Events.OnRedirectToIdentityProvider = ctx =>
    {
        ctx.ProtocolMessage.AcrValues = "idp:bankid";

        return Task.CompletedTask;
    };
});

Limitaitons of controlling authentication methods

It is important that the application only force specific authentication methods that all (valid) users can use.

Force re-authentication of a user

In some situations, you might want to force a user to log in again. One situation could be if sensitive data is to be changed.

We recommend that this is done by sending the OpenId Connect parameter prompt with the value login. The exact same behavior can be achieved by setting ForceAuthentication to true in a SAML application.

Another option is to use max_age with the value 0 (or another number to specify how long the time is accepted). Using this option will notify the user that the application requires a new login.

Example of using prompt

C# example

[HttpGet]
public IActionResult Reauthenticate()
{
    var properties = new AuthenticationProperties
    {
        RedirectUri = Url.ActionLink(nameof(Index))
    };
    properties.SetParameter<string>(OpenIdConnectParameterNames.Prompt, "login");
    return Challenge(properties);
}

By setting the parameter the OpenIdConnectHandler will automatically use the value.

Example of using max_age

C# example

[HttpGet]
public IActionResult Reauthenticate()
{
    var properties = new AuthenticationProperties
    {
        RedirectUri = Url.ActionLink(nameof(Index))
    };
    properties.SetParameter(OpenIdConnectParameterNames.MaxAge, TimeSpan.zero);
    return Challenge(properties);
}

By setting the parameter the OpenIdConnectHandler will automatically use the value.

Verify that the user has re-authenticated

It is possible to verify that the user have re-authenticated again by checking the auth_time claim.

C# example

var foundAuthTime = int.TryParse(User.FindFirst("auth_time")?.Value, out int authTime);

if (foundAuthTime && DateTimeOffset.UtcNow.ToUnixTimestamp() - authTime < MaxAgeAllowed)
{
}
else
{
}

Impersonate (run as) a user

This is only supported for OpenId Connect protocol.

Authway has support for impersonation (or run as) of another user for users with special permissions. A typical scenario is for a support organization to be able to run as the user to be able to see exactly the same things as that user. This is a very powerful functionality that should be used with caution and thoughtfulness. At minimum the application need to implement a way to show that the user right now is not using the application as her/himself but as another user and how the actions taken with someone else’s identity must be logged in an appropriate way. Authway always logs information about who is impersonating someone else, so there is some traceability there and this is also shown in audit logs.

Pre-requisites for impersonation

An application must be configured to allow impersonation to be able to use the functionality.

  1. There must be users that are granted the “Run as” or “Run as within my organisation” permission. These powerful permissions is not enabled for all tenants, so the first step is to enable it for the tenant that hosts support personal. Open “Security” module and look for “Run as” and/or “Run as within my organisation” functionalities. Open the functionality and add tenant permission. When this step is done, the functionality is available when setting permissions for a group (but only in selected tenant(s)).

  2. Open the application (that has implemented support for impersonation) and turn on support for “Allow impersonate”.

Implement support for displaying that a user is running as someone else (impersonate)

The application can detect that the user is in “Run as” mode by checking if there are a amr claim with the value “imp” (for impersonate). In that case there will also be a act claim, which contains the claims that represent the original user (serialized as JSON):

{
   "sub": "5d9b6b01-c038-4b8d-bd98-ac9d7a3d0d4d",
   "name": "Bagarn Olsson",
   "tid": "e23dfa1b-bf65-4a04-ac7c-44d9b3edc1bc",
   "act": {
      "sub": "243a7798-11cc-4856-866b-834d1c4c8dff",
      "tid": "da9140ca-9759-45c7-ad3a-4bc7dafca0d1"
   }
}

It is possible for the application to control which claims should be in the act claim by sending the parameter claims in the call to the IdP and there list the claim types that are desired with a space. If no claims parameter is passed with, act will contain sub (Subject or unique identity of the user) and tid (tenant id or unique identity of the tenant, which may be the same or different from the user’s tid depending on who you run as). Remember to limit the number of claims as it affects the size of the login ticket.

Trigger an impersonate sign-in

There are two main options for triggering the impersonate sign-in flow:

  1. Send the unique identity (sub value) of the user to impersonate
  2. Let the user search and choose user to impersonate in Authway

Both alternatives will use a custom acr_values parameter called impersonate.

Option 1: Send the unique identity (sub value) of the user to impersonate

This option requires that the application, triggering the impersonate sign-in flow, already knows the unique identity of the user that should be impersonated. Because of this, Option 2 might be easier to implement in most situations.

For this option the application sends impersonate:{sub} as acr_values.

C# Example

Add an Impersonate operation in a Controller:

[HttpGet]
public IActionResult Impersonate(string sub)
{
    var properties = new AuthenticationProperties
    {
        RedirectUri = "/"
    };
    properties.SetParameter(OpenIdConnectParameterNames.AcrValues, $"impersonate:{sub}");
    return Challenge(properties);
}

Handle RedirectToIdentityProvider event for the OpenIdConnectOptions so that the acr_values are send to Authway:

.AddOpenIdConnect(options =>
{         
    //Other configuration not displayed
    options.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Actor, JwtClaimTypes.Actor);

    options.Events.OnRedirectToIdentityProvider = ctx =>
    {
        if (ctx.Properties.Parameters.ContainsKey(OpenIdConnectParameterNames.AcrValues))
        {
            ctx.ProtocolMessage.AcrValues = ctx.Properties.GetParameter<string>(OpenIdConnectParameterNames.AcrValues);
        }

        return Task.CompletedTask;
    };
});

Option 2: Let the user search and choose user to impersonate in Authway

This is easiest to implement, since the application does not need to know anything about users. The application will rather just trigger the impersonate sign-in flow with the value select_account for the impersonate parameter.

C# Example

Add an Impersonate operation in a Controller:

[HttpGet]
public IActionResult Impersonate()
{
    var properties = new AuthenticationProperties
    {
        RedirectUri = "/"
    };
    properties.SetParameter(OpenIdConnectParameterNames.AcrValues, "impersonate:select_account");
    return Challenge(properties);
}

In the same way as above, the event RedirectToIdentityProvider for the OpenIdConnectOptions should be handled so that the acr_values are send to Authway:

.AddOpenIdConnect(options =>
{         
    //Other configuration not displayed
    options.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Actor, JwtClaimTypes.Actor);

    options.Events.OnRedirectToIdentityProvider = ctx =>
    {
        if (ctx.Properties.Parameters.ContainsKey(OpenIdConnectParameterNames.AcrValues))
        {
            ctx.ProtocolMessage.AcrValues = ctx.Properties.GetParameter<string>(OpenIdConnectParameterNames.AcrValues);
            //Request name claim (default is only sub and tid returned)
            if (ctx.ProtocolMessage.AcrValues.StartsWith("impersonate:"))
                ctx.ProtocolMessage.Parameters.Add("claims", "sub tid name"); 
        }

        return Task.CompletedTask;
    };
});

In this example the claims parameter is also send to request an extra claim (name) to be supplied in the act claim.

Revert an impersonate sign-in back to original user

When the user is done and want to return to her/himself again, the application should send impersonate: without value and then the user will be returned with tokens representing the original user (without act and amr claim with value “imp”).

It is also possible to directly run as yet another user by sending impersonate: select_account again (or one of the other options above). When doing so, Authway will first sign-out the current impersonation and then the original user will have to search for a new user to run as.

Force single-sign-on from a Client (Application)

This requires OpenId Connect protocol and it isn’t supported in Starter Edition.

There are scenarios where you might want to force a single-sign-on from a client, that has already identified the user. This requires a trust relation between the Identity Provider and the Client. The trust is established by using a shared secret (that must be protected and handled with care of course) and by configuring the client to be allowed to send a single-sign-on token.

Scenario: Internal/on-prem system that identifies users through a local AD sign-in

If an on-prem system/application has identified the users without using the Identity Provider, but wants to open an application that uses the Identity Provider to identify users, the user will not get a single-sign-on experience (since the user isn’t signed in at the Identity Provider). This can be solved if the application can create a SSO token that can be verified the Identity Provider.

Scenario: Application that uses refresh tokens for a long time

Applications that keep the user signed in by using refresh tokens to refresh the sign-in can have a signed-in user that isn’t signed in, in the Identity Provider anymore. For example if the Identity Provider is configured to keep the user signed in for 10 days and the application is confiugred for sliding refresh tokens and using them to keep the user signed in for 20 days. When this application then redirects the user to another application, the user will not ge a singe-sign-on experience.

Force a single-sign-on between applications

Authway has a non-standard support where applications can pass a SSO token that is validated by the Identity Server to force a user to be signed in.

  1. Create a SSO token (see below)
  2. Redirect the user to the application according to section 4 (“Initiating Login from a Third Party”) in OpenId Connect core specification. The iss parameter is required and should be the address to the Identiy Provider. Pass the serialized SSO token in a non-standard parameter sso_token. The sso_token must be passed on to the Identity Provider when the target application redirects the user to sign-in.

Step 2 can be completed in a non-standard way by making an agreement between the applications, as long as the sso_token is passed to the Identity Provider.

Create a SSO token

The SSO token must fullfil these requirements:

  1. A valid JWT token.
  2. Use the HS256 algorithm to sign the token.
  3. Hash the shared client secret with SHA256 and use a base64 encoded hashed value as key when signing the token.
  4. The issuer must be the client id that creates the SSO token.
  5. Include an audience claim with the Identity Provider as audience.
  6. Include an issued at (iat) claim with the time when the client created the SSO token.
  7. Include a sub claim with the unique identifier of the user that should be signed in.

Sample token (without signature):

{
  "alg": "HS256",
  "typ": "JWT"
}.
{
  "sub": "FEFC9E8B-062B-DF44-FDF0-39FC52AE2B58",
  "iat": 1674050819,
  "iss": "TestTrustedApp",
  "aud": "https://tenant.irmciam.se"
}

C# example of creating a SSO token

var subClaim = new Claim("sub", "FEFC9E8B-062B-DF44-FDF0-39FC52AE2B58"); //Get claim value from signed-in user
var clientId = "TestTrustedApp";
var clientSecret = "W7k1i3EvpYSApLj6CW7pYGkYsFTGdwJ96m0uIh64";
var authority = "https://tenant.irmciam.se";
var key = Encoding.ASCII.GetBytes(clientSecret.Sha256()); //= Client secret

var credentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature);
            
var tokenDescriptor = new SecurityTokenDescriptor {
    Subject = new ClaimsIdentity(new Claim[] { subClaim }),
    Issuer = clientId, 
    IssuedAt = DateTime.UtcNow,
    Audience = authority, 
    SigningCredentials = credentials
};
            
var tokenHandler = new JwtSecurityTokenHandler();

var token = (JwtSecurityToken)tokenHandler.CreateToken(tokenDescriptor);

var serializedToken = tokenHandler.WriteToken(token);
public static string Sha256(this string input)
{
    if (input.IsMissing()) return string.Empty;

    using (var sha = SHA256.Create())
    {
        var bytes = Encoding.UTF8.GetBytes(input);
        var hash = sha.ComputeHash(bytes);

        return Convert.ToBase64String(hash);
    }
}

Python example of creating a SSO token

This sample is still not verified.

This example uses of PyJWT (https://pyjwt.readthedocs.io/en/latest/usage.html).

import jwt
import base64
from hashlib import sha256

sub = "FEFC9E8B-062B-DF44-FDF0-39FC52AE2B58"
clientId = "TestTrustedApp"
clientSecret = "W7k1i3EvpYSApLj6CW7pYGkYsFTGdwJ96m0uIh64"
authority = "https://tenant.irmciam.se"
key = sha256(clientSecret.encode("utf-8"))
base64_bytes = base64.b64encode(key)
key = base64_bytes.decode("ascii")

payload = {"sub": sub, "iss": clientId, "aud": authority, "iat": 1674050819}

serializedToken = jwt.encode(payload, key, algorithm="HS256")

Limitations to be aware of

If a client passes another user, than the currently signed-in user (at the Identity Provider), the current user will be signed out at the Identity Provider, but this will not be a complete single-sign-out effecting applications until those are refreshed (because that isn’t possible to do during a sign-in).

The client must know the unique identifier of a user to be able to create the SSO token.

Switch linked user

In scenarios where linked users are used there can be situation where an application want to trigger a switch to another of the linked users then the one currently using the application.

If the application requests the org scope during sign-in a may_login claim is included in the claims for users that have other linked users. The may_login claim is a JSON object with an array of users that are linked to the current user.

Example of a may_login claim

Here is an example with typical claims in a token for a user that has two linked child users to a parent user.

"sub": "dd41355c-95d9-4bf1-9c21-523b5b40f9f4",
"oid": "e4b8a6ff-cdb1-45f8-b255-8df7a09a9596",
"tid": "567c9683-4603-4279-9e53-ed77b060fe72",
"companyname": "Organisation A",

"may_login": [
    {
        "oid": "dd41355c-95d9-4bf1-9c21-523b5b40f9f4", 
        "tid": "ffffffff-ffff-ffff-ffff-ffffffffffff", 
        "companyname": "Privatpersoner"
    },
    {
        "oid": "4a7b708d-b7d3-4931-b3be-1d86e72214a5", 
        "tid": "1b30a7a4-b271-493a-a315-d35e976f11cf", 
        "companyname": "Organisation B"
    }
]

Explanation of thoose claims:

  • The sub claim will be the same for all users. This is the definition of a linked user.
  • The oid claim will only exist for users that have a different unique identifier in Authway, than the sub claim. It will be emitted for child accounts, but not for the parent.
  • The tid claim identifies the current tenant id and the name of the organisation is in companyname claim.
  • The may_login contains two entries in the array and each entry has claims explained above.
  • The user in tenant “Privatpersoner” is the parent user. This can be concluded because the value of oid is the same as sub claim.
  • There are no information about the users eventual permissions to the application in the may_login claim. If that information is needed it can be requested through the admin APIs.

Trigger a switch to a specific user

To change the current user to one of the other two users that exists in the may_login claim can be done by passing either the tenant or the oid as parameter to the authorize endpoint.

The tenant id for the user is in the tid claim as explained above. So to switch to the user that belongs to “Organisation B” in the example above, the tenant id 1b30a7a4-b271-493a-a315-d35e976f11cf should be passed as explained in “Only allow users from a specific tenant to sign in”.

The other alternative is to pass the oid value as user_id parameter to the authorize endpoint. Here is an example where a typical re-direct to the autorize endpoint is extended with the user_id parameter with the value of oid claim from Organisation B in the example above:

https://YOURINSTANCE.irmciam.se/connect/authorize?
          client_id=YOUR_CLIENT_ID
          &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcallback
          &response_type=code
          &scope=openid
          &state=YOUR_STATE
          &user_id=4a7b708d-b7d3-4931-b3be-1d86e72214a5

User sign-out

Depending on your situation, sign-out can be a more complicated matter than you first think. Below we describe three scenarios and in some cases also choices that can be made in each scenario.

Single-sign-out

Authway supports single-sign-out, which pretty much handles sign-out the same way as single-sign-on works. When an application triggers a sign-out, Authway will check which applications the user is singed in too, and try to sign the user out from each of the applications. Authway supports single-sign-out for both OpenId Connect (OIDC) and SAML protocols. There are two different ways to notify server-side client applications that the user has signed out and the most commonly supported way is to use a front-channel which works for both OIDC and SAML, but for OIDC we recommend back-channel if possible.

Front-channel sign-out for server-side applications

The front-channel sign-out notification is done via the browser and this the only method supported for SAML sign-out. To use front-channel sign-out, the application should register a single URL that should be invoked by Authway when a user sign-out. A call to this URL will typically be handled in a way that the application deletes the cookie that keeps track of the fact that the user is signed-in. The URL is called from a hidden iframe on the signed out page in Authway.

Please note that Front-channel sign-out is broken when browsers block third party cookies. This is because in most scenarios the application cookie won’t be passed and not deleted in the iframe, since it will be a third party cookie.

There are other drawbacks with front channel. It is restricted to a signed in user’s browser to perform the request for sign-out, which makes it impossible to handle scenarios where a user or an administrator wants to force a sign-out from everywhere (including from other devices). This why we recommend back-channel sign-out if possible.

OIDC Front-Channel Logout 1.0 specification

Back-channel sign-out for server-side applications

The back-channel sign-out notification is done through a server-to-server call, where Authway POST a logout (JWT) token to the registered back-channel URL. This is generally not supported out of the box in OIDC libraries so you’ll have to implement it yourself. It is important that the posted token is correctly validated.

OIDC Back-Channel Logout 1.0 specification

Sign-out for browser-based JavaScript applications

For browser-based applications it is not necessary to make any configuration in Authway, but the application must perform monitoring on the check_session_iframe, which is implemented by libraries that are compliant with the OIDC specification, for example oidc-client JavaScript library.

Only sign-out from the Application (but not from Authway or other applications)

There are valid scenarios where the user only want to sign-out from the current application and not a full single-sign-out. If this is a desired scenario the application shall not invoke the sign-out functionality, through OIDC or SAML, in Authway, but rather just remove the user session in the application (aka delete the sign-in cookie).

One problem with this solution is that when the user chooses to sign-in again it will happen automatically since the session is still alive in Authway. This can be confusing for users as it feels like they were not signed out. To counteract that experience, you can force the user to sign in again (se below), but then single-sign-on is instead lost for the application.

The best solution can sometimes be to let users choose if they should sign-out from the application only or if they want a full single-sign-out from all applications where there account is currently used.

Force new sign-in

Authway has support to force a user to make a new sign-in. This can be configured for the application or it can be triggered by passing parameters from the application. To configure the application to always require a new sign-in the “Users SSO time” should be set to a low value, like 5 seconds. The other alternative is to control from the application if a new sign-in should be required or not. When using the OIDC protocol this is done by passing parameter max-age=0 and for SAML this is done by setting ForceAuthentication.

Sign-out from application and Authway (but no other applications)

Another alternative is to sign-out from the application and Authway, but not registering front-channel or back-channel for any applications. When signing out in Authway all refresh tokens are invalidated which in many scenarios still will result in a situation where users are forced to sign-in again, for example all applications that uses access- and/or refresh-tokens. Applications that only uses Authway for authentication can often handle this without affecting the users.

Sign-out without showing Authway signed out page

Some applications prefer to show their own signed out page after a sign-out instead of Authway signed out page. This is challenging to fulfil while still supporting OIDC and SAML protocols correctly (aka the front-channel sign-out). Authway can be configured (requires setting on the instance done by IRM) for automatic re-direct which will re-direct the user back to the application when front-channel sign-out is done. If the applications that the user is signed in to, does not have any front-channel URLs registered the redirect will be performed without showing Authway signed out page.

Subsections of User sign-out

Trigger OpenId Connect Sign-out

An application (relying party) that want to initiate a single-sign-out will do that through the library used for OIDC. If the library doesn’t support sign-out this is possible to trigger from the application anyway by making a re-direct (HTTP GET) to a URL that will be something like this:

GET https://instance.irmciam.se/connect/endsession?
post_logout_redirect_uri={URL TO RETURN USER TO AFTER SIGN OUT}&id_token_hint={ID_TOKEN]&
state={OPTIONAL STATE RETURNED TO CLIENT}
Parameter Requirement Description
post_logout_redirect_uri Recommended The URL where the user will be re-directed after a completing the sign-out. If parameter isn’t supplied a generic signed out message is displayed. The URL must be registered in application configuration for the application in Authway. If an exact match isn’t found the parameter will be ignored and the user will not be redirected back to the application in any way.
id_token_hint Recommended Indication of the user that should be signed out. This is required for post_logout_redirect_uri to be considered. If it isn’t supplied the user might be prompted to sign-out (for security reasons).
state Optional State information that is returned to the client when the user is re-directed after the sign-out. For example you can store information about current user session or a re-direct URL that the user should be returned to within the application after being returned to the post_logout_redirect_uri.

Full specification: OpenID Connect RP-Initiated Logout 1.0

C# example

In this example the application saves information about re-directing the user to Index action after a complete sign-out. We recommend that the local re-direct URL allows anonymous access, or otherwise a new sign-in will be triggered as a result of the sign-out and that will be a strange user experience.

public async Task SignOut()
{
    await HttpContext.SignOutAsync();

    string returnUrl = Url.Action(nameof(Index));
    await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme,
                new AuthenticationProperties { RedirectUri = returnUrl });
}

Handle OIDC back-channel notification

The URL registered in Authway for back-channel notification when a user signs out will receive an HTTP POST with a logout token (JWT). This JWT token must be correctly validated by checking at least these requirements:

  1. The token is signed by the keys published by Authway (can be found through metadata).
  2. The issuer should be the Authway instance (exact value for issuer is also available in metadata).
  3. The audience should be the client id of the application receiving the notification.
  4. A sub claim that uniquely identifies the user should be present.
  5. A sid claim should be present if the application settings is to include session index.
  6. A nonce claim should be present.
  7. A events claim should be present and it should contain a http://schemas.openid.net/event/backchannel-logout event.

By using the sub claim and optionally the sid the application should invalidate the user session. How this is done will differ depending on platform/application.

.NET Sample

A sample for .NET can be found here.

Subsections of Access control

Role-based access control

It’s common to use role-based access control, so even though it we recommend you to use permission-based access control, we will show what needs to be done for the application to check if a user belongs to a role (called group in Authway). To include the groups a user belongs to, you are required to add roles scope to your request.

HTTP/1.1 302 Found
Location: https://YOURINSTANCE.irmciam.se/connect/authorize?
          client_id=YOUR_CLIENT_ID
          &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcallback
          &response_type=code
          &scope=openid roles
          &state=YOUR_STATE

When doing so you will recieve one role claim for each group the user belongs too.

Even though this is straightforward to start with, there are several challenges with the role-based model that often result in many changes related to access control. For many features, it is common that many groups should have access, leading to very large if-statements. In a multi-tenant system (like the IdP, where each customer, reseller, partner, your own organisation itself, etc., is its own tenant), it’s also challenging to guarantee the names of the groups. For this, there is a concept of built-in group in IdP that cannot be renamed, and this must be used for role-based access control.

Example of using role-based access control

C# example

To include the roles a user belongs to, an additional scope and a claim-action must be added to the AddOpenIdConnect configuration:

options.Scope.Add("roles"); //Authway groups
options.ClaimActions.MapJsonKey("role", "role");

options.MapInboundClaims = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
    NameClaimType = "name", //Or your preferred claim for the user name
    RoleClaimType = "role"
};

With this additional configuration, claims of the type “role” will be retrieved, and since we had set RoleClaimType to “role” above, it means that the standard ways to check role membership will now work:

if (User.IsInRole("admin"))
    ...

eller:

[Authorize(Roles = "admin")]

Tips: IdentityModel is a package that has constants for many Claims which is a better way than using hardcoded strings as the examples above.

Permission-based access control

A system is built up of one or several functionalities. These functionalities are stable over time, meaning they exist as long as they are in the system, and they are never affected by organizational changes. Therefore, Authway has a concept of defining functionalities to which groups (or users) are granted access.

It is up to the system to define which functionalities need access control:

  • The system is a single functionality, meaning you either use the system or you don’t. It can also sometimes be divided into the right to read or the right to create/update/delete (administer).
  • One functionality per screen in the system.
  • One functionality for individual buttons in the system. An example could be a report, where one might have access for viewing (equivalent to a screen), but additionally has an extra functionality in the form of Export, where the content of the report can be exported. This could be more sensitive and therefore something one might want to control access for. In this case, you set up a function for “View Report X” and a function for “Export Report X”.

Functionalities can be managed directly in the Authway UI (User Interface), provided one has access to the “Manage Modules” functionality.

To include the permissions a user has, you are required to add perms scope to your request.

HTTP/1.1 302 Found
Location: https://YOURINSTANCE.irmciam.se/connect/authorize?
          client_id=YOUR_CLIENT_ID
          &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcallback
          &response_type=code
          &scope=openid perms
          &state=YOUR_STATE

When doing so you will recieve one perm claim for each functionality the user has access too.

Permission-based access control with .NET

Permission-based access control can be used just as easily in .NET as role-based access control. To enable this, we need to replace one of the build-in services and create an extension method. Below is example code for .NET how to implement permission-based access control.

To demonstrate how this can be used, we have defined a single functionality for an Example application, which is “Manage Order,” and we have given it the authorization name “Example.ManageOrder” in Authway. For this, we define the following additions in the Constants class:

public static class Constants
{
    public static class Permissions
    {
        public const string ManageOrder = "Example.ManageOrder";
    }
}

Now we need to expand/change which scopes are to be retrieved from the IdP and simultaneously define claim actions in the AddOpenIdConnect configuration:

options.Scope.Add("perms"); //Authway permissions
options.ClaimActions.MapJsonKey("perm", "perm"); //Authway permission claim

To make it easy to check if a user has authorization for a function in the system, for example, the following extension method can be created:

public static class PrincipalExtensions
{
    public static bool Authorize(this ClaimsPrincipal principal, string permission)
    {
        return principal.Identity.IsAuthenticated && principal.HasClaim(c => c.Type == "perm" && c.Value == permission);
    }
}

With the extension method in place we can easily show/hide the link to Manage order page (in _Layout.cshtml) based on the user’s access:

@if (User.Authorize(Constants.Permissions.ManageOrder))
{ 
<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="ManageOrder">ManageOrder</a>
</li>
}

Certainly, we can check if a user has access in other code in the same way. However, it would be nice if we could use the Authorize attribute as well, so we could do something like this in our OrderController:

[Authorize(Constants.Permissions.ManageOrder)]
public IActionResult Index()
{
    return View();
}

In .NET Core/.NET 5 and later, Microsoft has finally realized that role-based access control might not be ideal and, as a result, introduced an authorization system based on policies (read more here: https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies). To make it super easy to use our permission-based access control, we will create a custom PolicyProvider that automatically generates a policy to check authorization for a functionality, assuming the application hasn’t already defined a policy with that name:

/// <summary>
/// A policy provider that translates a policy name to a required permission with the same name, if not 
/// <see cref="DefaultAuthorizationPolicyProvider"/> already has provided a policy.
/// </summary>
public class PermissionAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
{
    /// <summary>
    /// Creates a new instance of <see cref="PermissionAuthorizationPolicyProvider" />.
    /// </summary>
    /// <param name="options">The options used to configure this instance.</param>
    public PermissionAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options) : base(options)
    {
    }

    /// <summary>Gets the default authorization policy or a permission based policy if no default is configured.</summary>
    /// <returns>The authorization policy.</returns>
    public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        var policy = await base.GetPolicyAsync(policyName);

        if (policy == null)
        {
            policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .RequireClaim("perm", policyName)
                .Build();
        }

        return policy;
    }
}

Finally we need to tell .NET to use our PolicyProvider instead of DefaultAuthorizationPolicyProvider, and we do this like any other configuration in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    //Add policy provider that generates a policy for each permission (if a policy is not found)
    services.AddTransient<IAuthorizationPolicyProvider, PermissionAuthorizationPolicyProvider>();
}

With that configuration, we can now use the Authorize attribute as mentioned above.

Subsections of Consume an API

Machine-to-Machine (M2M) authentication

There are many scenarios where applications, such as CLIs, or Backend services, need to call other APIs and when these APIs requires an access token the application must be able to authenticate itself (if the call isn’t done on behalf of a user). The Client Credentials Flow (defined in OAuth 2.0) allows an application to exchange its credentials, commonly a Client ID and Client Secret for an access token.

Exchange Client Credentials for an Access Token

To exchange the client credentials for an access token the Token endpoint is used.

POST /connect/token HTTP/1.1
Host: YOURINSTANCE.irmciam.se
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
 &client_id=THE_CLIENT_ID
 &client_secret=THE_CLIENT_SECRET
 &scope=THE_SCOPES_REQUIRED_TO_CALL_THE_API

C# Example

In this example code IdentityModel is used to simplify the code.

var client = new HttpClient();
var tokenRequest = new ClientCredentialsTokenRequest
{
     Address = "https://YOURINSTANCE.irmciam.se/connect/token",
     ClientId = "THE_CLIENT_ID",
     ClientSecret = "THE_CLIENT_SECRET",
     Scope = "THE_SCOPES_REQUIRED_TO_CALL_THE_API"
};
                
var response = await client.RequestClientCredentialsTokenAsync(tokenRequest);
                
if (response.IsError) throw new Exception(response.Error);
                
var token = response.AccessToken;

Use the Access Token

After retrieving the access token it should be added in the Authrozation HTTP header on all calls to the API:

Authorization: Bearer THE_ACCESS_TOKEN

The token is typically valid for a while (in many scenarios 1 hour), so re-use the token in multiple calls instead of retrieving a new for each call. This is extra important when many calls are done, since this will otherwise cause unnecessary load on Authway.

Call an API

It is common for a system/API to need to call another API within the same organization. These APIs are often protected in the same way, requiring a Bearer token for the request to be allowed. A Bearer token is sent in the HTTP header Authorization: Authorization: Bearer [ACCESS TOKEN].

The first decision to make is what the Access Token you’re sending should represent: a user or a system. When sending a token on behalf of a user, it’s referred to as Delegation, while when the system makes the call, it follows the Trusted Subsystem pattern.

Delegation: Making a call on behalf of a user

Delegation can be handled in two different ways. One common approach is what is sometimes called “poor man’s delegation,” which involves simply sending the user’s token directly to the underlying API. The other option is for the system making the call to perform a “token exchange” (OAuth Token Exchange specification, RFC 8693).

Regardless of the chosen model, it is necessary for your web application/API to access the user’s token. To enable this, .NET needs to store the token, which is done with the following configuration code in Startup.cs:

.AddOpenIdConnect(options =>
{
    ...
    options.SaveTokens = true;
    ...
});

Assuming this configuration is in place, it is possible to retrieve the user’s access token as follows:

httpContext.GetTokenAsync("access_token");

Poor mans delegation

The biggest advantage of this variant is that it is very easy to use. The only requirement is that all the API scopes that need to be used are included when the user’s access token is created. The downside of this solution is that if this access token falls into the wrong hands, it provides access to a larger attack surface than if it only gives access to a single API scope.

Typically, for this solution, you want to add the user’s access token to the requests made via HttpClient. A good way to handle this is to create a DelegatingHandler whose task is to do this, and configure HttpClient to use it. An implementation could look like this (available in IRM.Extensions.Http):

/// <summary>
/// A <see cref="HttpClient"/> handler that that adds a bearer token authentication header to the http request.
/// </summary>
public class BearerTokenAuthenticationHandler : DelegatingHandler
{
    private readonly IResolveBearerToken _resolveBearerToken;

    /// <summary>
    /// Creates a new instance of <see cref="BearerTokenAuthenticationHandler" />.
    /// </summary>
    /// <param name="resolveBearerToken">The <see cref="IResolveBearerToken"/> used to get a bearer token.</param>
    public BearerTokenAuthenticationHandler(IResolveBearerToken resolveBearerToken)
    {
        _resolveBearerToken = resolveBearerToken ?? throw new ArgumentNullException(nameof(resolveBearerToken));
    }

    /// <inheritdoc />
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await _resolveBearerToken.GetTokenAsync(request, cancellationToken).ConfigureAwait(false));
        return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
    }
}

The code above provides general support for adding a Bearer token and delegates the logic to retrieve the token itself to a class that implements IResolveBearerToken (also available in IRM.Extension.Http). For the “poor man’s delegation,” the implementation could look like this:

/// <summary>
/// Gets an access token from the current <see cref="HttpContext"/>.
/// </summary>
public class HttpContextBearerTokenResolver : IResolveBearerToken
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly ILogger<HttpContextBearerTokenResolver> _logger;

    /// <summary>
    /// Creates a new instance of <see cref="HttpContextBearerTokenResolver" />.
    /// </summary>
    /// <param name="httpContextAccessor">The <see cref="IHttpContextAccessor"/> used to get the <see cref="HttpContext"/> where the access token is retrieved from.</param>
    /// <param name="logger"></param>
    public HttpContextBearerTokenResolver(IHttpContextAccessor httpContextAccessor, ILogger<HttpContextBearerTokenResolver> logger)
    {
        _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <inheritdoc />
    public Task<string> GetTokenAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var httpContext = _httpContextAccessor.HttpContext ?? throw new InvalidOperationException("There is no HttpContext available which is required for HttpContextBearerTokenResolver.");
        _logger.LogInformation("Resolves bearer token by getting the access token from Httpcontext.");
        return httpContext.GetTokenAsync("access_token");
    }
}

Token Exchange

The current version of Authway does not have complete support for Token Exchange, but it can be easily added. In conjunction with that, certain implementation details can be discussed based on the desired objectives.

Trusted Subssytem: System-to-system

To use Client Credentials, you need to configure either a regular client (Configure an Application)(../configure-authway/configure-an-application) or an external system (Configure an External System)(../configure-authway/configure-an-external-system).

OpenID Connect provides the Client Credentials flow, designed for situations where one system needs to call another system. Typically, you require a client ID and a client secret to obtain an access token using these credentials. This access token typically contains protocol-specific claims. Since it doesn’t represent a user, common claims like sub, email, name, or, in Authway’s case, perm or role, are not present. When implementing an API, significant work is required to handle this, as it differs significantly from when a user calls the API. For example, regular authorization controls don’t work, and if you want to log who made a change and save the name/ID of the person performing the change, that information is not available either. Authway supports something called External Systems to address this challenge.

External Systems are clients with support for Client Credentials. In the background, an associated user is created with the same ID (sub) as the client ID. This enables treating the calling system similarly to a user.

Example to retrieve an access token according to Client Credentials

In the same way as for a user above, we use the BearerTokenAuthenticationHandler, but with a different implementation to extract the access token. This code has dependencies on the IdentityModel NuGet package.

/// <summary>
/// Gets a client credentials access token from an identity server.
/// </summary>
public class ClientCredentialBearerTokenResolver : IResolveBearerToken
{
    private readonly ClientCredentialBearerTokenResolverOptions _options;
    private readonly HttpClient _httpClient;
    private IMemoryCache _cache;
    private readonly ILogger<ClientCredentialBearerTokenResolver> _logger;

    private const string _cacheKey = "IRM.Extensions.Http.ClientCredentialBearerTokenResolver.Token";

    /// <summary>
    /// Creates a new instance of <see cref="ClientCredentialBearerTokenResolver" />.
    /// </summary>
    /// <param name="httpClient"></param>
    /// <param name="optionsAccessor"></param>
    /// <param name="logger"></param>
    /// <param name="cache"></param>
    public ClientCredentialBearerTokenResolver(HttpClient httpClient, IOptions<ClientCredentialBearerTokenResolverOptions> optionsAccessor, ILogger<ClientCredentialBearerTokenResolver> logger, IMemoryCache cache = null)
    {
        _options = optionsAccessor?.Value ?? throw new ArgumentNullException(nameof(optionsAccessor));
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));

        if (_options.CacheAccessToken && cache == null)
            throw new ArgumentNullException(nameof(cache));
        _cache = cache;
    }

    /// <inheritdoc />
    public async Task<string> GetTokenAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (_options.CacheAccessToken && _cache != null)
        {
            var cacheKey = $"{_cacheKey}:{_options.ClientId}";
            var token = await _cache.GetOrCreateAsync(cacheKey, async entry =>
            {
                var response = await GetTokenFromServerAsync(request, cancellationToken).ConfigureAwait(false);

                entry.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(response.ExpiresIn).AddSeconds(-20);
                return response.AccessToken;
            }).ConfigureAwait(false);

            return token;
        }
        else
        {
            var response = await GetTokenFromServerAsync(request, cancellationToken).ConfigureAwait(false);
            return response.AccessToken;
        }

    }

    private async Task<TokenResponse> GetTokenFromServerAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var tokenUrl = await GetTokenUrl(_options.Authority).ConfigureAwait(false);

        var tokenRequest = new ClientCredentialsTokenRequest
        {
            Address = tokenUrl,
            ClientId = _options.ClientId,
            ClientSecret = _options.ClientSecret,
            Scope = _options.Scope
        };
        _logger.LogInformation("Tries to resolve bearer token by getting the access token from {tokenUrl} for client {clientId}.", tokenUrl, _options.ClientId);
        var response = await _httpClient.RequestClientCredentialsTokenAsync(tokenRequest, cancellationToken).ConfigureAwait(false);

        if (!response.IsError)
            return response;

        if (response.ErrorType == ResponseErrorType.Exception)
            ExceptionDispatchInfo.Capture(response.Exception).Throw();

        string error;
        if (response.ErrorType == ResponseErrorType.Http)
            error = response.HttpErrorReason;
        else
            error = $"Error: {response.Error}\nDescription: {response.ErrorDescription}";

        _logger.LogError(error);
        throw new Exception(error);
    }

    private async Task<string> GetTokenUrl(string authority)
    {
        var discovery = new DiscoveryCache(authority, () => _httpClient);
        var disco = await discovery.GetAsync().ConfigureAwait(false);
        if (disco.IsError)
        {
            if (disco.Exception != null)
                ExceptionDispatchInfo.Capture(disco.Exception).Throw();

            throw new InvalidOperationException($"Failed to get token url. Details: {disco.Error ?? disco.HttpErrorReason}.");
        }

        return disco.TokenEndpoint;
    }
}

External system

In a basic configuration of Authway, it is still the case that an access token in the client credentials flow does not contain any user claims such as sub, name, and perm because that’s how the standard is supposed to work. There is an option to extend Authway with support that ensures these claims are included even in client credentials, but this must be done by IRM.

Instead, we recommend that the API retrieves this additional information from the Auth module when calls to the API arrive, for example, in a ClaimsTransformation class. The advantage of this approach is that the API itself can control how fresh (updated) claims it wants by managing caching, compared to when all claims are packed in the access token with the same lifespan as the token. Additionally, the token becomes smaller, and there are other advantages that you can read about in Privacy and GDPR. The same documentation also includes code examples of how to retrieve more information."

Protect an API

To protect an API, you must have configured an API.

To secure the API, configure it to require an access token:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
        {
            options.Authority = authority; //Base URL to Idp
            options.Audience = apiResource;

            options.TokenValidationParameters.ValidTypes = new[] { "at+jwt" };
            options.TokenValidationParameters.NameClaimType = JwtClaimTypes.Name;
            options.TokenValidationParameters.RoleClaimType = JwtClaimTypes.Role;

            // if token does not contain a dot, it is a reference token
            options.ForwardDefaultSelector = Selector.ForwardReferenceToken("introspection");
        })
    // reference tokens
    .AddOAuth2Introspection("introspection", options =>
    {
        options.Authority = authority;

        options.ClientId = apiResource;
        options.ClientSecret = apiSecret;
        options.EnableCaching = true;
        options.CacheDuration = TimeSpan.FromMinutes(20);
        options.NameClaimType = JwtClaimTypes.Name;
        options.RoleClaimType = JwtClaimTypes.Role;
    });

We have used the IdentityModel.AspNetCore.OAuth2Introspection NuGet package. Read more about ForwardDefaultSelector.

If you have API and web application in the same project, cookies are likely the default scheme used for authenticating requests. However, if you need to make pure API calls from, for example, a server application, you may encounter 401 errors. To address this, you can add a ForwardDefaultSelector to your Cookies configuration. This can be configured to check if the Authorization header starts with “Bearer” and, if so, set “Bearer” as the scheme to be used.

In IRM.AspNetCore.Authentication, there are ready-made extension methods to perform the typical configuration mentioned above.

Privacy and GDPR

All digital solutions must comply with privacy-related regulations such as GDPR. Therefor privacy design must be part of the implementation of Authway by IRM.

Examples of concerns that must be considered for a privacy design:

  • Consent management: organisations must obtain explicit consents from individuals before collecting and processing data.
  • Access control: great handling of users access and permissions.
  • Data minimization: an organisation should limit the amount of personal data they collect and process to only what is necessary for a specific purpose.
  • Data protection: an organisation is required to implement appropriate technical and organizational measures to protect personal data from unauthorized access, disclosure, alteration, or destruction.

Authway has a lot of support to fulfil a good privacy design, but it must also be correctly used by the applications. Below are things to consider to protect your users and their privacy.

Scopes

A scope represents a set of claims (user attributes) that an application is requesting when authenticating a user. Scopes provides a mechanism for applications (and APIs) to only receive the subset of user information that are necessary rather than receiving all of it.

Consider what claims is really needed and request just the scopes needed, so that the tokens will contain as little PII data as possible. Sometimes it is also worth re-configuring the scopes to contain fewer claims, in line with the user data your applications will use.

Authway also supports fetching additional scopes through a back-channel if necessary. There are several benefits with using a back-channel compared to request tokens with many claims:

  • Size. The tokens will be smaller which is often good since they are passed around a lot.
  • Security. The user data that is never put into the token can never be revealed.
  • Freshness of information. Tokens can sometimes be long-lived, but if the application (or API) uses a back-channel to retrieve additional scopes it will always get the latest updates.

User data in tokens

A JSON Web token (JWT) is a structured token that typically contains claims (data) about the user and some extra metadata. They are self-contained, signed for integrity of its data and the format is well defined so that an API can easily decode and verify the token without calling any other APIs. Since the user data is sensitive and subject to regulations, such as GDPR, we recommend the use of Reference tokens, so that access tokens are only a random string and therefor can’t reveal any PII data. This requires the APIs to support both JWT and reference tokens, but it is usually not so much extra work. It is also possible to put the logic to exchange the reference token for a JWT token in an API Gateway, reverse proxy or any other middleware.

The reference tokens is exchanged for a JWT token by calling the Introspection endpoint of Authway. For performance reasons the exchanged token can be cached until it expires. This solution is fully compliant with OAuth 2 standard so no proprietary solutions is required for either the application or the API to use reference tokens.

Never send the token to a web browser

Store user cookies on the server

Application unique user identifiers

Export personal data

Authway has pre-build support for downloading a file with the personal data that exists in the service. This file can be reached from {auth domain}/identity/manage/download, but it requires a signed in user. The same information can exported from the Person admin API. If a more customized export is need, all information could be gathered by calling our different admin APIs to fully customize how the information is exposed to the user.

Delete personal data

We have several automized clean-up jobs that remove personal data that can be enabled and configured.

The automated jobs will not be enough in many situations and complementary tools are necessary. The admin UI of course supports a manual remove of persons and their data which also can be used directly by your end-customers. Everything that can be done in the UI can be automated by calling our admin APIs.

One challenge with a shared service for users, is that it can be hard to know when a user can/should be removed. An example could be a user that has access to system A, B and C. When system B decides that the user should be removed (could be build in or decided by a centralized rule engine) there is no way for system B to decide if the user should be removed from Authway. To handle this situation Authway have a specialized API where system B can request to remove all permissions to the system for a user. This together with the clean-up job to remove users that has no permissions can solve this challenge.

API to remove all permissions for a module

HTTP DELETE /api/users/{userId}/permissions/modules/{moduleName}

If userId is unknown it is possible to search for user by username. The API will remove the user from all groups that has permissions that belong to the module. It will also remove user specific permissions that belongs to the module.

Signing keys

Authway uses public-key cryptography to sign ID tokens, access tokens and SAML assertions, so that an application (client) that uses Authway to authenticate users can trust the result. A signing key is a JSON web key (JWK) that consists of a public and privat key pair. The private key is used by Authway to create a signature that can be verified with the well-known public key.

By default, Authway uses RSA keys for RS256 signing algorithm.

A JSON web key set (JWKS) is a set of keys containing the public keys used to verify the signature in your application. The set can contain one or more public keys:

{
    "keys": [
        {
            "kty": "RSA",
            "use": "sig",
            "kid": "NOT THE REAL VALUE",
            "e": "NOT THE REAL VALUE",
            "n": "NOT THE REAL VALUE",
            "alg": "RS256"
        }
    ]
}

The JWKS can be fetched from the Identity Server at the relative URL .well-known/openid-configuration/jwks, but the preferred way to retrieve the URL for the JWKS is to read the jwks_uri from the metadata document (found at .well-known/openid-configuration). Within the set there can be one or more keys for one or more signing algorithms (if more than one algorithm is configured).

How it works

When a user signs in, Authway typically creates tokens with information about the signed-in user. The tokens are signed with the private key, before they are send to your application. To verify that the the token you retrieve is valid and issued by the Identity Server, your application uses the public key.

It is important that you use the jwks_uri endpoint to get the keys dynamically since the keys can change over time. Of course the keys should be cached, but it is a good practices to refresh the cash once every day.

Note that validation of the signature is only one aspect of validating a JWT and expiration, issued time, audience and issuer are examples of more necessary validation. We recommend the use of platform support for token validation is used since it is a complex and security critical process to do correct.

Key rotation

Key rotation is an Enterprise feature of Authway, but since this can change (both what is included in different price levels, but also what version is used) we strongly recommend each application to implement support for key rotation. The keys can also be changed in the case of a security breach.

It is a good practice to rotate the keys regularly and Authway rotates them every 90 days (by default, but it can be configured). The new key is announced 14 days in advance, and retained for 14 days after it expires. The first key in the list is the default key:

{
    "keys": [
        {
            "kty": "RSA",
            "use": "sig",
            "kid": "NOT THE REAL VALUE",
            "e": "NOT THE REAL VALUE",
            "n": "NOT THE REAL VALUE",
            "alg": "RS256"
        },
        {
            "kty": "RSA",
            "use": "sig",
            "kid": "NOT THE REAL VALUE",
            "e": "NOT THE REAL VALUE",
            "n": "NOT THE REAL VALUE",
            "alg": "RS256"
        }
    ]
}

If the signature couldn’t be verified with the default key, you should try to verify the signature with the other key(s) for the same signing algorithm.

Signing algorithms

By default Authway uses RS256 signing algorithm, but it is possible to configure support for RS, PS and ES family of cryptographic signing algorithms. When multiple algorithms is supported is is possible to override the default on a per resource and client basis.

Tokens

There are primarily two types of tokens related to identity; ID tokens and Access tokens.

ID Tokens

ID tokens are JSON web tokens (JWT) that an application uses to retrieve information about a user. You shouldn’t use ID tokens when calling an API.

Access Tokens

Access tokens are used when calling an API to prove that the bearer of the token is allowed to call the API and what parts of the API is allowed to be used by the consumer.

Special Tokens

Refresh tokens is a special token, used to renew an access token without forcing the user to re-authenticate.

Subsections of Tokens

ID Tokens

The ID Token is conceptually the same as an identity card, with information about a user packaged as a JWT token. ID Tokens can be returned as a result when an application makes an OpenId Connect (OIDC) authentication request to the Identity Provider. The statements (called claims) of an ID Token are packaged in a simple JSON object (this is the Body of the JWT token).

  {
   "iss": "https://instance.irmciam.se",
   "sub": "f404a4ff-6037-4097-afa7-24b397239009",
   "tid": "d10a1624-6de7-4b50-b662-0f1cc6733edf",
   "aud": "APPLICATION CLIENT ID",
   "nonce": "VALUE",
   "exp": 1311281970,
   "iat": 1311280970,
   "auth_time": 1311280969,
   "amr": "pwd"
  }

An ID Token has these features:

  • Asserts the identity of the user, called subject in OIDC and represented with a sub claim.
  • Asserts the identity of the tenant, represented with a tid claim. This is not standardized or required in the protocol, but a claim that Authway always issues.
  • Specifies the issuing authority, represented with a iss claim.
  • Is generated for a specific application, called client in OIDC and represented with a aud claim.
  • Has an issue (iat claim) and expiration time (exp claim).
  • May contain other protocol claims like
    • A nonce that associates a client session with the token, which is used to mitigate replay attacks.
    • One or more authentication method reference (amr claim) that represents how the user was authenticated. There are standardized values that Authway uses if it exists.
  • May contain other claims about the user, like name and email, depending on what scopes was requested.

The ID token header, claims JSON and signature are encoded into a base 64 URL-safe string, so it can easily be passed around.

Considerations to make for ID Tokens

What claims end up in the ID Token is controlled by passing Scopes to the Authorization endpoint. The openid scope is required when requesting an ID Token, but other can be requested to retrieve more information about the user. In Authway the openid scope contains the sub and tid claims.

When requesting many scopes the returned ID Token will contain many claims, which can result in a large ID Token. The size of the token can effect how good it is to keep in a cookie or other state for the client application. It is good to keep the token as small as possible. It is possible to request more information about the user from the UserInfo endpoint instead. The downside of that alternative is that it results in more network requests which can affect the latency of the application during sign-in.

Access Tokens

Refresh Tokens

Refresh tokens are used to retrieve a new access token for a user without requiring the user to interactively authenticate again. A refresh token is typically long-lived (from days up to a month is common), but they enable the creation of short-lived (from minutes to hours) access tokens. This is good from security perspective since anyone that is in possession of an access token is allowed to call API:s that the token has access too and by giving them a short life-time the consequence of a stolen access token is less than if it is long-lived.

Get a refresh token

It is easy for the application to request a refresh token, since all that is necessary is to add the offline_access scope to the scope parameter list when making the authorization request.

Since it is a privileged operation to use a refresh token, the client must be configured to allow offline access. In client administration it is also possible to configure for how long a refresh token is valid and other settings.

Use a refresh token

The refresh token is used to retrieve a new access token (and/or ID token) for a user without re-authenticating the user. This should be done a short while before the access token expires (and not each time you need an access token) so that you always have an access token that are valid when calling the API.

To get a new access token, the refresh token is send to the token endpoint and the result is a token response containing a new access token, its expiration and potentially a new refresh token.

POST /connect/token
    client_id=client&
    client_secret=secret&
    grant_type=refresh_token&
    refresh_token=the_refresh_token

Security Consideration

Refresh tokens are extremally valuable (because they are long-lived) and must be carefully protected by the clients that are allowed to use them.

Refresh Token Rotation

By default Authway rotates the refresh token on each usage so that the refresh token only can be used once. This reduces the attack surface because there is a chance that a stolen token becomes unusable before the attacker have used it.

Since this is the default configuration for a client, it is important that the client takes this in consideration when using the refresh token. The client is responsible to synchronize the renewal between threads (so the refresh token isn’t used multiple times) and since the second use of a refresh token will result in a response with status code 400, it is a risk that the client invalidates the user session.

For .NET developers we recommend you to use Duende Access Token Management which has support for many token management tasks.

Revoke Tokens

Subsections of Integrations

Admin APIs

Everything that can be done in Admin UI (and a little bit more) can also be done through API calls. This makes it possible to embed administration into other applications and completely customize the UI for administration.

Some of the APIs are limited to a single tenant, for example it is only possible to handle users for a single tenant. The following API:s are bound to a tenant:

  • Person
  • Users
  • Groups
  • External systems
  • Organisations (depending on permissions)

For external systems and users that have the special permission (manage organisations) to configure tenants it is possible to control which tenant each API call should be handled for by passing the X-IRM-TenantId HTTP Header with the unique identity (UUID) as value.

Within the Users APIs there are special /me endpoints which requires an access token of an actual end-user to be used. Since they represent an actual user, there is no need to pass any Tenant Id.

All other APIs are configuration APIs and instead requires very specific permissions to be used.

Important information about the unique identifiers (GUID:s)

Many APIs allows you to pass a unique identifier in the form of a GUID (UUID), but we recommend you to not do that and instead let Authway create an optimized Id, which is returned in all responses to create APIs. If you need to pass the Id from your code we strongly recommend you to use an algorithm that creates identifiers optimized for the underlying database, or else the performance of the system will be negatively impacted. We can provide you with such algorithm for SQL Server.

Errors from the APIs

We try to use reasonable status codes for different situations in the API. When we return a body with error information, the body follows the Problem Details for HTTP APIs (RFC 7807) standard.

Status Code Description Details
400 Errors in the provided data. This will always have a problem details body with more information about the error.
401 Failed to authenticate the call.
403 The user or client calling the API is missing the required permission.
404 The URL is not matching any API or the id:s in the URL does not match any existing object. This will often have a problem details body with more information about the error.
500 An internal server error. Of course we make our best to not end up with a 500 error, but it could for example be a dead lock situation or that we for some reason couldn’t reach the database at all and so on.
501 Not implemented or supported. Very uncommon, but there are scenarios where you can call the API with values that are not incorrect, but still not supported. Another situation is when trying to use an API for a feature that is not part of the bought service. In those cases, we have chosen to give a 501 instead of a 400. This could happen during development, but should never be a case in production if the consumption of the API have been tested correctly, but 400 errors could exist in production and be caused by end-user data.
503 Infrastructure problem. Most likely temporarily.

Subsections of Admin APIs

Deprecated APIs

Below is a list of APIs that will be deleted in a future version together with suggestion what to do (if there are any).

2024-10-05

  • api/tenants/{tenantId}/modules/{moduleId}/buy is replaced with api/tenants/{tenantId}/modules/{moduleId}/activate
  • api/users/{userId}/invitationlink resp. api/users/{userId}/resetlink will retire { "initationLink"="https://....." } respectively { "resetLink"="https://....." } and instead now use a shared model:
{
    "link"="https://....."
    "validTime"="3.00:00:00"
}

2024-07-10 (removed from 1.2.139, 2024-10-16)

This is a breaking change and we have contacted each customer to ensure a quick timeline until this is fixed.

  • api/persons will be bound to the current user tenant (or the tenant in HTTP header X-IRM-TenantId). Instead the CreatePersonViewModel contained a TenantId which is still there for compatibility reasons, but should not be used anymore.

2023-05-15 (removed from verison 1.2.110, 2024-09-23)

  • api/tenants/{tenantId}/module is replaced with api/tenants/{tenantId}/modules
  • api/group/* is replaced with api/groups/*
  • api/groups/{groupId}/user is replaced with api/groups/{groupId}/users

OAS 3.0

Events API

Authway has events for many of the things that happens in the service. You can tap into these events and write your own logic by registering a webhook or by using our Event API. Note that webhook feature is only available in the Enterprise edition of the service.

Common Properties in all events

Each event payload also contains properties unique to the event. You can find the unique properties in the individual event documentation.

Name Type Description
AggregateId UUID The unique identifier of the aggregate (information entity) that the event happended for.
OwnerId UUID The unique identifier of the owner (tenant) that the event belongs to.
EventId UUID The unique identifier for the event. Can be used for idempotency, logging and more.
Occured DateTime The date and time (in UTC) when the event occurred.
CausedByPersonId UUID The unique identifier of the user that caused the event (if any).
CausedBy string The name of the user that caused the event (if any).
TraceId string The trace id that can be used to trace different actions that is going on in a system.

IpAddressLocation Properties

Depending on environment and configuration some events will include location information based on the IP address. Sources for this information can also be different when hosting your-self compared to Authway IDaaS.

Name Type Description
CountryCode string The two-letter country code (3166-1) representing the country.
Country string The name of the country.
Region string The name of the region.
City string The name of the city.
Latitude decimal The latitude.
Longitude decimal The longitude.

When an IpAddressLocaiton exist in the event, we garuantee that the CountryCode and Country is set, but the other properties will depend on information availability in the source.

Available Events

Organisation Events

To retrieve all organisation events you use the topic “organisation”.

Person Events

To retrieve all organisation events you use the topic “person”.

User Events

To retrieve all organisation events you use the topic “user”.

Organisation module Events

To retrieve all organisation module events you use the topic “organisationmodule”.

Module Events

To retrieve all module events you use the topic “module”.

Subsections of Events API

Subsections of Organisation Events

OrganisationClaimAdded

An organisation got an organisation claim added. To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.organisationclaimadded”.

Name Type Description
AggregateId UUID The unique identifier of the organisation.
ClaimType string The type of claim.
ClaimValue string The claim value.

OrganisationClaimRemoved

An organisation got an organisation claim removed. To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.organisationclaimremoved”.

Name Type Description
AggregateId UUID The unique identifier of the organisation.
ClaimType string The type of claim.
ClaimValue string The claim value.

OrganisationCreated

An organisation is created. It is a new tenant if the ParendId does not have any value (and GroupMotherId and AggregateId is equal). To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.organisationcreated”.

Name Type Description
AggregateId UUID The unique identifier of the organisation.
GroupMotherId UUID The id of the group mother (root organisation) in the organisation tree. For the group mother, this value will be the same as Id.
ParentId UUID The identity of this tenants parent, if any.
Name string The name of the organisation
IdentityNumber string The identity number for the organisation.

TrustedDomainRemoved

An organisation got a trusted domain removed. To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.trusteddomainremoved”.

Name Type Description
AggregateId UUID The unique identifier of the organisation.
Domain string The domain that was removed.

TrustedDomainAdded

An organisation got a trusted domain added. To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.trusteddomainadded”.

Name Type Description
AggregateId UUID The unique identifier of the organisation.
Domain string The domain that was added.

OrganisationUpdated

An organisation is updated. To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.organisationupdated”.

Name Type Description
AggregateId UUID The unique identifier of the organisation.
Name string The name of the organisation
IdentityNumber string The identity number for the organisation.

OrganisationDeleted

An organisation is deleted. To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.organisationdeleted”.

Name Type Description
AggregateId UUID The unique identifier of the organisation.

Subsections of Person Events

PersonCreated

A person is created. It is common for UserCreated to be created at the same time, but it is possible to create a person who are not a user. To retrieve only this event you use the topic “person/irm.aspnetcore.identity.events.personcreated”.

Name Type Description
AggregateId UUID The unique identifier of the person.
OrganisationId UUID The id of the organisation that the person is added to.
FirstName string The first (given) name of the person.
LastName string The last (family) name of the person.
Email string The email for the person. This can be a different email than the username and/or user email.

PersonDeleted

A person is deleted. When a person is deleted, so is all user and person events for that person. The metadata is preserved, but no payload data will be available again. To retrieve only this event you use the topic “person/irm.aspnetcore.identity.events.persondeleted”.

Name Type Description
AggregateId UUID The unique identifier of the person.

PersonUpdated

A person is updated. To retrieve only this event you use the topic “person/irm.aspnetcore.identity.events.personupdated”.

Name Type Description
AggregateId UUID The unique identifier of the person.
OrganisationId UUID The id of the organisation that the person belongs to.
FirstName string The first (given) name of the person.
LastName string The last (family) name of the person.
Email string The email for the person. This can be a different email than the username and/or user email.

Subsections of User Events

UserCreated

A user is created. This can happen multiple times for the same user id, since it is possible to delete only the user and then re-create the user. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.usercreated”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
Username string The username which is unique within the tenant, but can exists for multiple tenants.
Email string The email address of the user. This is typically the same as Username, but the service can be configured to use PhoneNumber or any username and in thoose cases it can differ.
EmailConfirmed bool True if the email is confirmed; otherwise false.
PhoneNumber string The phone number of the user. In a default configuration phone number is not visible and collected, so it is common for this to be null.
PhoneNumberConfirmed string True if the phone number is confirmed; otherwise false.
ValidFrom DateTime The date and time (in UTC) when the user is valid (aka the earliest point in time when the user is allowed to sign-in).
ValidTo DateTime The date and time (in UTC) when the user is valid (aka the latest point in time when the user is allowed to sign-in). Commonly null.
IsSystemUser bool True if this user represents a system (aka an external system); otherwise false.
SendInvitation bool True if an invitation will be send to the user; otherwise false.
AdditionalInvitationParameters string Additional parameters that should be added to the invitation link. Typically it can contain for example a client id to brand the create account page for a specific client.
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserActivated

A user account have been activated, which can be immediately or at Valid from. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.useractivated”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).

Note that there can be a delay until the event is pushed, but it will always be pushed in correct order.

UserUpdated

A user is updated. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userupdated”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
Username string Obsolete.Use UserUsernameChanged event to handle changes of the username instead.
Email string The email address of the user. This is typically the same as Username, but the service can be configured to use PhoneNumber or any username and in thoose cases it can differ.
EmailConfirmed bool True if the email is confirmed; otherwise false.
PhoneNumber string The phone number of the user. In a default configuration phone number is not visible and collected, so it is common for this to be null.
PhoneNumberConfirmed string True if the phone number is confirmed; otherwise false.
ValidFrom DateTime The date and time (in UTC) when the user is valid (aka the earliest point in time when the user is allowed to sign-in).
ValidTo DateTime The date and time (in UTC) when the user is valid (aka the latest point in time when the user is allowed to sign-in). Commonly null.
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserUsernameChanged

The username of a user is changed. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userusernamechanged”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
Username string The username which is unique within the tenant, but can exists for multiple tenants.
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserDeleted

A user is deleted. The user can be deleted, without deleting the person, which also results in that it is possible to re-create the user with the same id. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userdeleted”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).

UserDeviceAdded

An user signed in from a new device. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userdeviceadded”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
DeviceId string The unique identifier of the device.
FromIpAddress string The IP Address of the user that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.

UserDeviceCountryAdded

An user signed in from a known device, but a new country. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userdevicecountryadded”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
DeviceId string The unique identifier of the device.
FromIpAddress string The IP Address of the user that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.

UserInvited

A user is invited to create an account. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userinvited”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserLoginAdded

An user added an external login to the account. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userloginadded”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
LoginProvider string The login provider added.
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserLoginRemoved

An user removed an external login from the account. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userloginremoved”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
LoginProvider string The login provider removed.
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserPasswordAdded

An user added a password. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userpasswordadded”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserPasswordChanged

An user changed the password. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userpasswordchanged”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserPasswordRemoved

An user removed the password from the account. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userpasswordremoved”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserRoleAdded

A user is added to a role. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userroleadded”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
NormalizedRoleName string The unique and normalized role name that the user was added to.
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserRoleRemoved

A user is removed from a role. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userroleremoved”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
NormalizedRoleName string The unique and normalized role name that the user was removed from.
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserSignInAssociated

A user has associated a sign-in from an invitation to create an account. This also means that the email address is confirmed. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.usersigninassociated”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
AuthenticationMethod string The type of authentication that the user used when associating an authentication method.
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserSignedIn

A user signed in. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.usersignedin”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
Kind int Indicates what kind of situation that caused the event. Can be one of these values: 0. Interactive sign-in where user is fully aware; 1. Automatic sign-in where the user is signed-in automatically by single-sign-on; 2. Refresh where an application uses a refresh token to re-new the user sign-in; 3. Impersonate which is when another user impersonates the user.
AuthenticationRequirement string The requirment of the authentication process for the user, for example “1FA” or “2FA”. This will only be set when Kind is 0 or 3.
AuthenticationMethod string The type of authentication that the user used when signing in. This will only be set when Kind os 0 or 3.
FromIpAddress string The IP Address of the user (or service) that caused the event. When kind is 2 (Refresh) this will be the IP Address of the server, not the user, since it is performed over a backchannel.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event. Will be the user’s web browser user agent for all Kind except when Kind = 2 (Refresh) which happens in a backchannel.
Metadata dynamic A dynamic object with additional data for the event.

More about Metadata

Metadata is a dynamic object that can contain different extra properties that might vary on Kind, protocol used or other factors.

When the event occurs as a result of using OpenId Connect it will include these:

Name Type Description
ClientId string The unique identifier of the client that asked to sign-in the user.
ClientName string The name of the client that asked to sign-in the user.

The JSON will be like this:

{
   ...
   metadata: {
      clientId: "UniqueClientId",
      clientName: "The perfect client"
   }
}

When the event occurs as a result of a user impersonating another user it will include these:

Name Type Description
ImpersonatedByUserId UUID The unique identity of the user impersonating the user.

The JSON will be like this:

{
   ...
   metadata: {
      impersonatedByUserId: "UniqueUserId",
   }
}

UserSignedOut

An user signed out. This event only happens when a user activly signs out, and not because of a timeout of a valid sign-in. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.usersignedout”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserSignInFailed

An user failed to sign in. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.usersigninfailed”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.
Reason numeric One of the following values indicating the reason to why the sign-in failed: 0. Invalid credentials, 1. Locked-out user, 2. Inactive user, 3. Impossible travel, 4. Module not activated for tenant or 5. Module is offline.
BreachedPasswordUsed bool true if a breached password have been used for this failed sign-in; otherwise false. If password wasn’t used or if breach detection is not configured this will be null.

UserLockedout

A user account have been locked (commonly because to many sing-in attempts). To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userlockedout”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserUnlocked

A user account have been un-locked. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userunlocked”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserDeactivated

A user account have been de-activated, which is at Valid to. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userdeactivated”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).

Note that there can be a delay until the event is pushed, but it will always be pushed in correct order.

UserReactivated

A user account have been activated again after beeing de-activated, which can be immediately or at Valid from. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userreactivated”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).

Note that there can be a delay until the event is pushed, but it will always be pushed in correct order.

UserConfirmedEmail

An user confirmed the email address. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userconfirmedemail”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

UserConfirmedPhoneNumber

An user confirmed the phone number. To retrieve only this event you use the topic “user/irm.aspnetcore.identity.events.userconfirmedphonenumber”.

Name Type Description
AggregateId UUID The unique identifier of the user (always the same as the person id for end-users).
FromIpAddress string The IP Address of the user (or service) that caused the event.
IpAddressLocation IpAddressLocation Ip address information if available.
UserAgent string The user agent string from the browser (or service) that caused the event.
Metadata dynamic A dynamic object with additional data for the event.

Subsections of Organisation module Events

ModuleActivatedForOrganisation

An organisation got a module activated. To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.moduleactivatedfororganisation”.

Name Type Description
AggregateId UUID The unique identifier of the activated module and organisation combination.
OwnerId UUID The unique identifier of the organisation.
ModuleId UUID The unique identifier of the module.

ModuleInactivatedForOrganisation

An organisation got a module in-activated. To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.moduleinactivatedfororganisation”.

Name Type Description
AggregateId UUID The unique identifier of the in-activated module and organisation combination.
OwnerId UUID The unique identifier of the organisation.
ModuleId UUID The unique identifier of the module.

ModulePayedForOrganisation

An organisation got a module set as payed (after beeing unpayed). To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.modulepayedfororganisation”.

Name Type Description
AggregateId UUID The unique identifier of the payed module and organisation combination.
OwnerId UUID The unique identifier of the organisation.
ModuleId UUID The unique identifier of the module.

ModuleUnpayedForOrganisation

An organisation got a module set as un-payed (the effect is that users from that organisation can’t sign-in until set as payed again). To retrieve only this event you use the topic “organisation/irm.aspnetcore.identity.events.moduleunpayedfororganisation”.

Name Type Description
AggregateId UUID The unique identifier of the unpayed module and organisation combination.
OwnerId UUID The unique identifier of the organisation.
ModuleId UUID The unique identifier of the module.

Subsections of Module Events

ModuleWentOffline

A module went offline. To retrieve only this event you use the topic “module/irm.aspnetcore.identity.events.modulewentoffline”.

Name Type Description
AggregateId UUID The unique identifier of the module.

ModuleWentOnLine

A module went online. To retrieve only this event you use the topic “module/irm.aspnetcore.identity.events.modulewentonline”.

Name Type Description
AggregateId UUID The unique identifier of the module.

FunctionalityDeleted

A functionality is deleted. When a functionality is deleted, the permission is also deleted from Group templates, Groups and External Systems. To retrieve only this event you use the topic “module/irm.aspnetcore.identity.events.functionalitydeleted”.

Name Type Description
AggregateId UUID The unique identifier of the module.
FunctionalityId UUID The unique identifier of the funcitonality.
Permission string The unique identifier of the permission.

Webhooks

Authway uses webhooks to automatically notify your application any time certain changes happens in Authway.

Webhooks can be set up per event stream, where a stream is responding to a major information object for examples person, user or organisation. It is possible to set up multiple webhooks for a single stream and naturally it is possible to set up webhooks for different streams. The webhooks are grouped in a webhook system. The webhook system will typically be one for the consuming application. If one webhook is failing for a system, all webhooks for that system is stopped, which can affect how you choose to group your webhooks.

By default you will only be able to receive events that is owned by the same tenant as the Webhook is registered for.

Handling events

Choose event to recieve

When configuring a webhook, you can use the API to choose which events will send you payloads. This is done by setting a topic to the stream or a specific event. Only subscribing to the specific events you plan on handling limits the number of HTTP requests to your application. You can also subscribe to all current and future events. By default, webhooks are only subscribed to the future events. You can change the topic at any time, but for an existing webhook, it will only affect future events.

Advanced Topics

A topic is flexible and you can combine more than one “thing” in a topic by separating each identifier with “,”. For example:

Topic Description
person, user/irm.aspnetcore.identity.events.usersignedin All events for person and UserSignedIn event.
user/irm.aspnetcore.identity.events.usersignedin, user/irm.aspnetcore.identity.events.usersignedout, user/irm.aspnetcore.identity.events.usersigninfailed The three events UserSignedIn, UserSignedOut and UserSigninFailed.
person, user All events for both person and user.

Respond immediately to events

When receiving an event to your webhook, it is strongly recommended to respond with a 200 Ok (or another 2XX status code) as quickly as possible. Failing to do so might trigger re-tries which can increase the load of your application more than necessary.

A common pattern to handle webhook events effectivly is to add the event body on a message queue and immediately respond with a success status code. The internal queue can then be processed by a background worker.

Re-tries of failed events

For a configured webhook we guarantee that subscribed events are delivered at least once and in correct order (per webhook). If we get a timeout, a network failure or if we receive a response with status code 408 or any 5XX we’ll retry to send the payload again. Authway uses an exponential backoff scheme when doing re-tries and if all re-tries fails the webhook system will stopped. When this happens we will notify you by the registered e-mail address. It is your responsibility to re-start a stopped webhook system when your application is ready to handle the events again.

If Authway is causing the failure, we will also re-start the webhook system after we have corrected the issue.

Ignoring duplicate events

It is possible to receive the same event more than once, so we recommends that you handle webhook events using idempotent operations. One way of doing this is logging the EventId of the events that you have processed and ignoring subsequent requests with the same EventId.

Order of events

Authway guarantees that events within a webhook is delivered in the same sequence that they are happening. Between webhooks there are no guarantees of ordering, so your logic should handle cases where events are delivered out of order. If you create separate webhook systems, the ordering of the events between these systems is also not guaranteed.

Securing webhooks

Validating events are from Authway

Since the webhook receiver is available on the public Internet it is very important that you validate the incoming request before processing it. We recommend that you start by checking that all three custom HTTP headers are available. From security perspective the X-IRM-Signature header is the important one. Use your secret (that you used during registration) to calculate a HMAC-SHA256 signature of the received body.

C#:

using System;
using System.Security.Cryptography;
using System.Text;

class WebhookSignature
{
   public static string ComputeSignature(string data, string secret)
   {
       HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
       byte[] stringBytes = Encoding.UTF8.GetBytes(data);
       byte[] hashedValue = hmac.ComputeHash(stringBytes);
       return Convert.ToBase64String(hashedValue);
   }

   public static void Main (string[] args)
   {
       Console.WriteLine(ComputeSignature("messageBody", "apikey"));
   }
} 

PHP:

function compute_signature($data, $secret) {
    return base64_encode(hash_hmac('sha256', $data, $secret, true));
}

echo(compute_signature('messageBody','apikey')); 

Require authentication

Authway supports Basic authentication when calling your webhook. You provide the username and password when registering the webhook.

Obfuscating webhook URL

A minor improvment in the security of webhooks is to make the endpoints harder to guess. This can be done by adding a series of random numbers and letters to your endpoint URL. For example https://api.yourapplication.com/webhooks/j490smlkfs034jld94jlae045 is harder to guess than https://api.yourapplication.com/webhooks.

Testing Webhooks

It is not necessary for you to immediately create och deploy a service that can receive webhook payloads. Instead we recommend you to start with ay of the service on Internet that allows you to receive HTTP POST calls, for example https://webhook.site/. That service makes it really easy to get something up and running, and you can easily see the webhook payload directly in your browser.

To start receiving webhooks you must first start (activate) the webhook system by calling api/systems/{systemId}/start. Trigger some events in the source system and you should soon see POST arriving in the browser.

You can also stop the pushing of new events by calling api/systems/{systemId}/stop. If you want to simulate that the system have been stopped because of a failure you can use the same API. Use the source system to create some more events and then start the system again, and you should see all events be posted until it catches up to the last existing event.

You can also get information about the last date/time and event id that have been successfully posted to your webhook by making a get request to api/systems/{systemId}.

Exposing your Local Machine to the Internet

Since all webhooks are pushed over Internet, you’ll have to expose your local machine somehow, if you want to receive webhooks during development. We recommend that you use for example Ultrahook or ngrok to do that.

After configuring a service you’ll have to reconfigure your webhook system to receive payloads on your new URL.

Subsections of Webhooks

Webhook Events and Payload

Webhook Events and Payload

Webhook Common Properties in the Payload

Each webhook event payload also contains properties unique to the event. You can find the unique properties in the individual event documentation.

Name Type Description
AggregateId UUID The unique identifier of the aggregate (information entity) that the event happended for.
OwnerId UUID The unique identifier of the owner (tenant) that the event belongs to.
EventId UUID The unique identifier for the event. Can be used for idempotency, logging and more.
Occured DateTime The date and time (in UTC) when the event occurred.
CausedByPersonId UUID The unique identifier of the user that caused the event (if any).
CausedBy string The name of the user that caused the event (if any).
TraceId string The trace id that can be used to trace different actions that is going on in a system.

Delivery Headers

The HTTP POST payloads send to your webhook’s configured URL endpoint will contain the following custom HTTP headers:

Header Description
X-IRM-Signature A base64 encoded HMAC-SHA256 signature of the body, signed with your API key (secret).
X-IRM-Topic The topic that you registered for this webhook encoded as base64 (UTF8).
X-IRM-EventType A base64 (UTF8) encoded full name of the event, guaranteed to be unique.

Also, the User-Agent for the requests will be IRM-Webhook.

Subsections of Migration

Migration of Users With Passwords

We support migrations of users and their passwords if we can implement the same hash algorithm that is currently used for creation and validation of their passwords. After an old/migrated password is validated the password will be re-hashed to use our modern hashing solution to protect the passwords.

If the hashing alorithm is unknown or is not possible to implement, users can still be migrated and an e-mail can be send to all users with a link where they can create a new password (or use another sign-in alternative). We can also implement more customized migration strategies based on you requirements.

Existing hash algorithm support

We currently have existing support for MembershipHasher (if you are migrating from ASP.NET Membership) and SimpleMembershipHasher (if you are migrating from ASP.NET Simple Membership).

Other migration support

If you have admin UI:s that uses ASP.NET Roles, we have a custom AuthwayRoleProvider that can be used with small modifications on existing code to talk to our admin API:s. Just let us know and we’re happy to share this with you.

The changes needed is likely to handle the fact that Authway is multi-tenant, but Roles (and Membership) have no concept of multi-tenancy. This might force code changes to use are extended methods to pass in the TenantId that should currently be handled or configure a single TenantId that the existing admin UI should work against.

Subsections of System Administrator Guide

Privacy and GDPR

Introduction

All digital solutions must comply with privacy-related regulations such as GDPR. Therefor privacy design must be part of the implementation of Authway by IRM.

Examples of concerns that must be considered for a privacy design:

  • Consent management: organisations must obtain explicit consents from individuals before collecting and processing data.
  • Access control: great handling of users access and permissions.
  • Data minimization: an organisation should limit the amount of personal data they collect and process to only what is necessary for a specific purpose.
  • Data protection: an organisation is required to implement appropriate technical and organizational measures to protect personal data from unauthorized access, disclosure, alteration, or destruction.

There are many parts to this design, for example how applications use PII data from Authway (outlined in Developer guide) and below is description of built-in clean up tasks that are run automatically if turned on. Consider which of these tasks that would be good to use for your situation.

Remove user audit information

This task is always active in all Authway instances and by default audit information older than 365 days are removed. We can configure for how many days the audit information should be kept.

Remove inactive organisations

The task will remove an inactive organisations which is necessary for GDPR reasons since some organisations can include personal information.

An inactive organisation is defines as an organisation that:

  • does not have any users
  • does not have any persons
  • does not have any subsidaries

The following options can be configured for this task:

  • Minimum age: The number of days that should pass before a newly created organisation is evaluated as inactive.
  • Kind of organisation: can be only subsidiaries, group mothers (tenants) or both.

Remove inactive users

The task will remove an inactive user since GDPR does not allow an organisation to keep PII data longer than necessary.

The following options can be configured for this task:

  • Maximum number of days since last sign-in. Default 365 days.
  • Type of organisations to search for inactive users. Can be Private persons, Organisations or Both.
  • Import date: can be used to not remove users that are created before an import date.

Remove users that have never signed-in

The task will remove a user that has never signed-in. This is especially useful to remove users that have created accounts but never verified e-mail/phone number and therefor haven’t been able to sign-in (when verification is required). In those situations the user account should be removed earlier than the user is considered inactive.

The following options can be configured for this task:

  • Minimum age: The number of days that should pass before a newly created user will be removed.
  • Type of organisations to search for inactive users. Can be Private persons, Organisations or Both.
  • Import date: can be used to not remove users that are created before an import date.

Remove users without any permissions (belong to any group)

The task will remove a user that does not belong to any group and therefor does not have any permission to access anything.

The following options can be configured for this task:

  • Maximum number of days since last sign-in. Default 365 days.
  • Type of organisations to search for inactive users. Can be Private persons, Organisations or Both.
  • Import date: can be used to not remove users that are created before an import date.

Linked users

Linked users are users in different tenants that all will emit the same unique identifier in the sub claim. The concept can be used in situations where you want to treat different users as a single user no matter which tenant the user signing in belongs too.

Imaging an organisation that have private persons (consumers) as their primary customer. Some of this users also needs to be able to verify that they belong to an organisation to get access to specific applications/services that are oriented towards organisations (and not consumers). Even when the users sign in to an organisation, they still want to be able to get data about the user in their common consumer role. An example could be a sports club where a private person buys tickets and does other interaction in their consumer role, but some of them also sponsors the sport club with their company. As a sponsor the user might be entitled to special applications/services that only sponsors are allowed to use. In this situation the user must be able to prove that they belong to the organisation sponsoring, but it is likely that the user also want easy access to their tickets and other information belonging to their private person profile. This is situations that linked users can help solve.

Linked users will always see all sign-in alternatives for all their users, no matter which username was used to start the sign-in flow. If the same sign-in alternative is used on all users, they will have to choose in which context/tenant they want to sign-in. This can be automatically be done, for example if an application passes tenant information.

Sign-in flow for linked users

For linked users it is likely that one or the other has access to the module the user is signing in to. Authway will try to automatically pick the user with access and also try to switch user if already signed in to the one missing access. The following rules are applied during sign-in:

  • Sign-in alternatives are limited to the user that has access to the module
    • If no user (with the supplied username) have access to the module, the sign-in flow will continue until we display that access is missing (which leaves the possibility to change username).

Limitations of linked users

Linked users comes with the following limitations (or rules):

  • The users that should be linked, must belong to different tenants.
  • If linked user is created with same credentials as parent, all credential changes will be applied on both accounts.
  • It is not possible to link users in hierarchies.
  • The parent user can not be deleted until all linked users are deleted.
  • System users (external systems) can’t be used in linking.
  • If a sign-in is matching two users in the same tenant, the sign-in will always be done with the one where the username matches. For example if a user have the username [email protected] in both Private persons tenant and in Company A tenant, but also have a [email protected] user that is linked to [email protected] in Private person. The scenario also requires that exactly the same password is used on all three users. When signing in into Company A the service will match two users and to select which one to actually sign-in a new match on the username will be performed.

Lägga till inloggning med Microsoft Entra ID (eller Microsoft-konto)

Det finns AddMicrosoftEntraID-metod som sätter upp ett antal inställningar som vi av erfarenhet är bra för att inloggningen ska fungera bra med IRM.AspNetCore.Identity, t.ex. en felhantering som gör att användaren får veta varför det blev problem. Alla dessa inställningar går att göra om i en egen konfiguration av options, plus att det finns en del nödvändiga inställningar som måste göras, som att ange ClientId och ClientSecret.

services.AddAuthentication()
        .AddMicrosoftAzureAd(options =>
        {
             options.ClientId = "ClientId from portal";
             options.ClientSecret = "Secret from portal";

             //The Prompt property can be used to force a login, or the consent screen. Per specification, “none”, “login”, “consent”, 
             //“select_account” values can be used here. If the login is not forced, the user will automatically be logged in, if only one 
             //account is active.
             //options.Prompt = "select_account";
         });

Synkronisera användarnas grupper från Entra ID med externa grupper

Vi har inbyggt stöd för att synkronisera de säkerhetgrupper som en användare tillhör i Entra ID med de grupper som är konfigurerade som externa i grupp-administrationen. För att man ska få med grupperna måste följande konfiguration görs under App Registreringen i Azure Portal:

  1. Öppna konfigurationen för din app som identifieras av det client id som du använt ovan.
  2. Klicka på “Token Configuration”
  3. Klicka på “Add groups claim”
  4. Välj “Security groups”.
  5. Under ID token väljer du Group ID och kryssar i “Emit groups as role claims”.

Nu kommer användarens Entra ID säkerhetsgrupper komma med i form av det objekt id de har i Entra ID. Det innebär att när du konfigurerar en Extern grupp ska du ange objekt id som det externa gruppnamnet.

Configure Authentication Methods for a Tenant

Each Authway instance have at least Username/password and Microsoft Authentication configured, but there can also be more authentication methods such as social logins, verified identities such as Swedish BankId, One-time Password and others. All instance configured authentication methods can be turned off for a specific tenant if it shouldn’t be allowed. It is also possible to add tenant specific authentication methods for OpenId Connect or SAML protocols.

Tenant specific authentication is possible to configure after registering a unique identifier for the tenant that will be part of the URLs during sign-in flow. Use tenant name of an abbreviation of the name.

Auto-provisioning

Auto-provisiong is the concept of automatically handle the complete life-cycle of users in their home catalog (realm). It has the following benefits:

  • Single-sign-on with their existing business accounts.
  • During sign-in all information about the user, such as name and e-mail, is updated from the information provided by their home catalog.
  • Automatic synchronization of user permissions. This is done by synchronizing the Authway groups the user should belong to by matching the external groups with the groups/roles that the user belongs too in their home catalog.
  • Automatic creation of new users and assignment of groups in the same way as above. This can be done by registering trusted domain(s) for the tenant. The trusted domain should match everything after the “@” character in the e-mail address that the user enters when signing in.

For auto-provisioning to work at least one of the allowed authentication methods on the tenant must be configured to allow auto-provision and also one or more trusted domains must be configured.

Auto-linking

Auto-linking is very similar to auto-provisioning, with the difference that a user can’t be automatically created. Instead the user must be created by the administrator or through API calls. Auto-linking does not require any trusted domains to be configured for the tenant.

The benefits of using auto-linking is very similar to auto-provision except for initial creation of users.

Social Sign-in Methods

When using social sign-in methods, such as Microsoft, Google, Apple and other, we recommend that the privacy policy includes information about this. For some of them, for example Google, it is required. The information we suggest is included can be something like this:

" When you use a social login, such as Microsoft, Google [include the ones used], we collect a unique identifier, your name and e-mail address from the service. "

Google

We have somewhat limited possibilities to configure Google authentication, because of the requirements set by Google. Our only possibility is to administrate the simplest form of integration, without brand recognition. If you want to display your logo and/or a specific application name, the configuration must be done with from Google accounts that has access to Google Search Console where your domain is verified (which means this also requires a custom domain for you Authway instance). We can of course help and guide in the process, or be invited with enough permissions to your Google Console.

Subsections of Configure Authentication Methods for a Tenant

Configure Password Requirements for a Tenant

It is possible to configure stronger (but not weaker) password requirements for a tenant, than the Authway instance is configured with. For example if the instance is confugred to require a password length of at least 6 charachters, it is possible to require the password to be at least 8 charachters in a specific tenant. Choose the “Local” authentication method to configure password requirements.

Configure Microsoft Entra ID for a Tenant

We recommend all organisations that uses Microsoft Entra ID (former Azure AD) to use it for their users. The process of using Microsoft Entra ID is very simple:

  1. Register the tenant id for your Microsoft Entra ID. This is displayed on the Overview page of Microsoft Entra ID administration. If you don’t have administrativ priviliges in Entra ID, please ask you administrator.
  2. Approve our pre-configured Microsoft Entra application in your Entra ID instance. The configuration page for Microsoft authentication method has a link to consent the application for your convenience.

After thoose simple steps all your users can use their existing business accounts to sign-in. To synchronize permissions from your Microsoft Entra ID to the applications that uses Authway you follow theses steps:

  1. Navigate to Groups in Authway admin.
  2. Choose an existing group or create a new group.
  3. Set the group to be external and set the external name to be the Object ID of the Microsoft Entra ID group. Object ID is displayed both in the listning of all groups and on the Overview page of the specific group.

Use your own Microsoft Entra application

Some organisations wants to provide their own Microsoft Entra application to use for their users. One reason for doing this could be to filter the groups exposed. To create an application to use in Authway do this (some of the steps are equal to Configure OpenId Connect for a Tenant):

  1. Navigate to the organisation in Authway admin portal.
  2. Choose “Settings” and then “Add” -> “OpenId Connect”.
  3. Edit the settings for the newly created OpenId Connect authentication method, typically named the same as you organisation name.
  4. Set “Authority” to https://login.microsoftonline.com/[Your Microsoft Entra Tenant Id]/v2.0/.
  5. Copy the redirect URL.
  6. Open Microsoft Entra ID administration portal
  7. Choose “App registrations” in the left pane and click “New registration”.
  8. Give your application a suitable name and let the default “Accounts in this organizational directory only” be selected for “Supported account types”.
  9. Under “Redirect URI”, choose “Web” as platform and paste the copied redirect URL (from above in the Authway admin portal).
  10. Copy the application id (client id) from Microsoft Entra portal and paste it as Client Id in Authway.
  11. Create a client secret in Microsoft Entra portal and copy the secret value so you can paste it as Client Secret in Authway.
  12. Enable and save the tenant specific authentication in Authway. It can take up to five minutes before it becomes available as a sign-in alternative.
  13. To get an Entra application that handles everything the pre-configured application does, you also need to add some optional claims under “Token configuration”. Click “Add optional claim”, choose “ID” as Token type and select family_name plus given_name. Click “Add” and accept that the “Microsoft graph profile permission” is required. This configuration ensures that Authway will get and can synhronize its first and last name properties. Finally, to flow permssions, you also need to “Add groups claim”. Mark “Security groups” and optionally do additional configuration, but the pre-configured client uses Group ID (which will be the Object ID of the group). sAMAccoountName can be used when Entra is synhronized with an on-prem AD and will in that case emit the name of the group instead of Object ID, which is more readable as the external group name in Authway.

Filter the Groups Exposed to Authway

A common reason to creat a custom Entra application is to filter the groups exposed to Authway. One way to do this is during “Add group claim” above, but if you for example want to expose all groups that has a group name that starts with “authwayTest” you can do this:

  1. On the App configuration, click on “Manifest” in Microsoft Entra portal and set "acceptMappedClaims" to true. Save the application.
  2. Make sure to consent the application configured above.
  3. Click on “Enterprise applications” in Microsoft Entra ID admin portal and search for your newly created application.
  4. On the deatils for the application, select “Single sign-on”.
  5. Click “Edit” in the “Attribute & Claims” section.
  6. Click on the groups claim (under “Additional claims”). In the window that opens, it is possible to choose “Filter groups”. Do that, choose “Display name” as attribute, “Prefix” for “Match with” and finally enter the prefix, for example “authwayTest”.

Entra-group-filter.png Entra-group-filter.png