Integrationsunderlag

Det här dokumentet är självbärande. Det hänvisar inte vidare, och innehåller allt som behövs för att koppla en applikation till Aegis Central ID.

Är du en människa och vill ha en guide att läsa i ordning, börja på Kom igång. Är du ett verktyg som ska generera kod, läs vidare här.

Fasta värden

De här ändras inte och kan skrivas in direkt.

Namn Värde
Utfärdare (issuer, authority) https://login.aegiscentralid.se/
Upptäcktsdokument https://login.aegiscentralid.se/.well-known/openid-configuration
Publika nycklar (JWKS) https://login.aegiscentralid.se/.well-known/jwks
Auktoriseringsändpunkt https://login.aegiscentralid.se/connect/authorize
Tokenändpunkt https://login.aegiscentralid.se/connect/token
UserInfo https://login.aegiscentralid.se/connect/userinfo
Utloggning https://login.aegiscentralid.se/connect/logout

Föredra alltid upptäcktsdokumentet framför att skriva in enskilda adresser. Biblioteket hämtar dem självt, och de kan ändras utan att integrationen går sönder.

Det enda som ska bytas ut

DITT-CLIENT-ID — hämtas från arbetsytan på https://login.aegiscentralid.se/workspace/clients.

Är klienten konfidentiell finns också DIN-CLIENT-SECRET, som visas en enda gång när klienten skapas. Den ska läsas ur en miljövariabel, aldrig skrivas in i kod.

Allt annat i exemplen nedan kan lämnas som det står.

Så bestäms callback-adressen

Callback-adressen är en adress på applikationens egen sajt dit användaren skickas tillbaka efter inloggning. Aegis bestämmer den inte — applikationen väljer den, och registrerar den sedan i arbetsytan.

Regler som gäller alltid:

  1. Absolut adress. https://din-sajt.se/callback, inte /callback.
  2. https krävs. Enda undantaget är localhost, för utveckling.
  3. Inget fragment. Adressen får inte innehålla #.
  4. Exakt matchning. Adressen i auktoriseringsanropet måste vara tecken för tecken identisk med den registrerade. …/callback och …/callback/ är två olika adresser.
  5. Registrera varje miljö för sig. Utveckling och drift är olika adresser och måste båda läggas in.

Konventionell sökväg per ramverk

Välj den som ramverket redan förväntar sig — då behöver ingenting konfigureras om.

Ramverk Konventionell sökväg Registrera alltså
ASP.NET Core /signin-oidc https://din-sajt.se/signin-oidc
Node, openid-client valfri, ofta /callback https://din-sajt.se/callback
PHP, openid-connect-php filens egen adress https://din-sajt.se/callback.php
Python, Authlib valfri, ofta /callback https://din-sajt.se/callback
Ren webbsida, embed.js valfri sida som anropar Aegis.completeLogin() https://din-sajt.se/callback

För lokal utveckling: samma sökväg men mot http://localhost:<port>.

Regler som Aegis upprätthåller

En integration som bryter mot något av det här får ett fel, inte en tyst avvikelse.

  • Authorization Code Flow. response_type=code. Implicit flow finns inte.
  • PKCE krävs av alla klienter, även konfidentiella. Endast S256; plain avvisas.
  • state ska skickas och jämföras vid återkomsten.
  • En kod kan bara lösas in en gång. Löses samma kod in två gånger återkallas hela kedjan, inklusive refresh token.
  • MFA är obligatoriskt för alla konton. Applikationen behöver inte hantera det.

Scopes

Scope Ger
openid Obligatoriskt. Ger sub.
profile name, preferred_username
email email, email_verified
tenant.role tenant, tenant_id, tenant_role
offline_access Refresh token

Klienten måste ha fått scopet tilldelat i arbetsytan för att kunna begära det.

Anspråk i ID-token

{
  "iss": "https://login.aegiscentralid.se/",
  "aud": "DITT-CLIENT-ID",
  "sub": "ba9715c3-7638-45b4-dd64-08df0232cc27",
  "exp": 1787617406,
  "iat": 1787615606,
  "name": "Anna Lindqvist",
  "preferred_username": "anna@exempel.se",
  "email": "anna@exempel.se",
  "email_verified": "true",
  "tenant": "assethub",
  "tenant_id": "01a035fc-6e64-705d-bcb2-28a85e446ed4",
  "tenant_role": "Owner"
}

sub är användarens permanenta id. Använd det som nyckel i din databas, inte e-postadressen — adressen kan bytas, sub ändras aldrig.

tenant_role är Owner, Admin eller Member, och gäller den valda arbetsytan.

Vad koden måste verifiera

Biblioteket gör det åt dig. Stäng inte av något av det:

  • iss är https://login.aegiscentralid.se/
  • aud är ditt eget klient-id
  • Signaturen validerar mot JWKS
  • exp har inte passerat
  • state matchar det som skickades

ASP.NET Core

// Program.cs
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = "aegis";
})
.AddCookie()
.AddOpenIdConnect("aegis", options =>
{
    options.Authority = "https://login.aegiscentralid.se/";
    options.ClientId = "DITT-CLIENT-ID";
    options.ClientSecret = builder.Configuration["Aegis:ClientSecret"];
    options.ResponseType = "code";
    options.UsePkce = true;

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");
    options.Scope.Add("tenant.role");

    // Motsvarar den registrerade adressen https://din-sajt.se/signin-oidc
    options.CallbackPath = "/signin-oidc";

    // false: tokens hålls utanför kakan. Med true blir svaret från callbacken
    // större än de 4 kB huvuden många omvända proxyer buffrar → 502.
    options.SaveTokens = false;
});

Har applikationen redan ASP.NET Core Identity ska Aegis i stället registreras som extern inloggning med SignInScheme = IdentityConstants.ExternalScheme, utan att röra Identitys förvalda scheman, och en ExternalLogin-sida ska koppla sub till ett konto i AspNetUsers via AddLoginAsync. Har applikationen en egen kakinloggning utan Identity tas resultatet i stället över i Events.OnTicketReceived med context.HandleResponse() och applikationens egen SignInAsync. Fullständig kod för båda: https://docs.aegiscentralid.se/aspnet-core.md.

Står applikationen bakom en TLS-avslutande proxy krävs UseForwardedHeaders med proxyns adress i KnownProxies, annars byggs redirect_uri med http:// och avvisas.

Node

// npm i openid-client
import * as client from "openid-client";

const config = await client.discovery(
    new URL("https://login.aegiscentralid.se/"),
    "DITT-CLIENT-ID",
    process.env.AEGIS_CLIENT_SECRET);

const verifier = client.randomPKCECodeVerifier();
const challenge = await client.calculatePKCECodeChallenge(verifier);
req.session.verifier = verifier;

res.redirect(client.buildAuthorizationUrl(config, {
    redirect_uri: "https://din-sajt.se/callback",
    scope: "openid profile email tenant.role",
    code_challenge: challenge,
    code_challenge_method: "S256"
}).href);

// På /callback
const tokens = await client.authorizationCodeGrant(
    config,
    new URL(req.url, "https://din-sajt.se"),
    { pkceCodeVerifier: req.session.verifier });

const claims = tokens.claims();

PHP

<?php
// composer require jumbojett/openid-connect-php
use Jumbojett\OpenIDConnectClient;

$oidc = new OpenIDConnectClient(
    'https://login.aegiscentralid.se/',
    'DITT-CLIENT-ID',
    getenv('AEGIS_CLIENT_SECRET'));

$oidc->setRedirectURL('https://din-sajt.se/callback.php');
$oidc->addScope(['openid', 'profile', 'email', 'tenant.role']);
$oidc->setCodeChallengeMethod('S256');
$oidc->authenticate();

$sub  = $oidc->getVerifiedClaims('sub');
$roll = $oidc->getVerifiedClaims('tenant_role');

Python

# pip install authlib flask
from authlib.integrations.flask_client import OAuth

oauth = OAuth(app)
oauth.register(
    name="aegis",
    server_metadata_url="https://login.aegiscentralid.se/.well-known/openid-configuration",
    client_id="DITT-CLIENT-ID",
    client_secret=os.environ["AEGIS_CLIENT_SECRET"],
    client_kwargs={
        "scope": "openid profile email tenant.role",
        "code_challenge_method": "S256",
    })

@app.route("/login")
def login():
    return oauth.aegis.authorize_redirect("https://din-sajt.se/callback")

@app.route("/callback")
def callback():
    token = oauth.aegis.authorize_access_token()
    anvandare = token["userinfo"]
    return anvandare["sub"]

Ren webbsida utan backend

<script src="https://login.aegiscentralid.se/embed.js"
        data-client-id="DITT-CLIENT-ID"
        data-redirect="https://din-sajt.se/callback"
        data-scope="openid profile email"></script>

<div class="aegis-signin"></div>

På callback-sidan:

<script src="https://login.aegiscentralid.se/embed.js"
        data-client-id="DITT-CLIENT-ID"
        data-redirect="https://din-sajt.se/callback"></script>
<script>
Aegis.completeLogin()
    .then(function (tokens) { /* tokens.id_token, tokens.access_token */ })
    .catch(function (fel) { console.error(fel.message); });
</script>

Har applikationen en server ska servern göra tokenutbytet. Tokens i webbläsaren kan läsas av varje skript på sidan.

Andra språk

Aegis följer OpenID Connect Discovery. Vilket certifierat bibliotek som helst fungerar med tre inställningar:

  1. Utfärdare eller metadata-adress enligt tabellen överst.
  2. response_type=code.
  3. PKCE med S256.

Vanliga fel

Fel Betyder
invalid_request, client_id saknas Frågesträngen tappades på vägen
invalid_client Fel klient-id, eller fel miljö
invalid_grant vid inlösen Fel code_verifier, koden redan använd, utgången, eller redirect_uri som inte matchar
invalid_grant vid förnyelse Refresh token återkallad. Användaren måste logga in igen
Redirect-adressen godtas inte Matchar inte tecken för tecken, eller använder http