OpenSSO
Udviklerguide (OpenID Connect)

Log ind med Unilogin - udviklerguide

Denne guide er til softwareudbydere, læringsportaler og udviklere af egne webapps, der vil tilbyde "Log ind med Unilogin" uden selv at have en aftale med STIL.

Hvad det er

OpenSSO er en OpenID Connect-udbyder. Vi understøtter Authorization Code-flowet, hvor PKCE med S256 er påkrævet. Brugeren logger ind med Unilogin hos STIL via OpenSSO, og jeres app modtager et id_token. I behøver ingen aftale med STIL.

Sådan kommer I i gang

  1. Bed om en integration via kontaktformularen eller på info@opensso.dk.
  2. I får et client_id og et client_secret.
  3. I oplyser jeres præcise redirect-URI'er (https) og eventuelle post-logout-URI'er. Kun registrerede adresser accepteres.

Discovery og endpoints

De fleste OpenID Connect-biblioteker kan konfigurere sig selv ud fra discovery-dokumentet:

https://opensso.dk/.well-known/openid-configuration
EndpointFormål
/oidc/authorizeStarter login: brugeren sendes hertil (authorization endpoint).
/oidc/tokenVeksler authorization code til id_token og access_token (kun POST).
/oidc/userinfoReturnerer claims for access_token (Bearer).
/oidc/jwksOffentlige nøgler (JWKS), som id_token verificeres mod.
/oidc/logoutAfslutter sessionen hos OpenSSO og sender brugeren tilbage til jer.

Klientautentifikation og tokens

Scopes og claims

Scopes skal være godkendt på jeres integration. Scopes, I ikke har fået godkendt, udelades stille.

ScopeClaims
openidsub
profilename, given_name, family_name
uniloginuniid
skoleaktoer_gruppe, institution_ids, inst_brugere, elevtrin, hovedgruppenavn, roller, grupper, has_license

Privatliv og pseudonymisering

sub er altid pairwise og pseudonym: en tilfældig, stabil ID pr. bruger pr. app, som gemmes i OpenSSOs database. Samme elev får forskellig sub i forskellige apps, og jeres app kan ikke se Unilogin-brugernavn eller navn, medmindre scopes unilogin eller profile er godkendt på integrationen.

Det betyder, at en app kan nøjes med scope openid og alligevel genkende brugeren næste gang - uden egen "anonymiserings"-server eller database til ID-kobling.

Eksempel 1: rå HTTP-flow

Først genereres en PKCE-verifier og -challenge (gem verifier, state og nonce i brugerens session):

# code_verifier: 43-128 tegn (a-z A-Z 0-9 - . _ ~), gemmes i sessionen
CODE_VERIFIER=$(openssl rand -base64 48 | tr '+/' '-_' | tr -d '=\n')
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')

Send derefter brugeren til authorize-URL'en (linjeskift er kun for læsbarhed):

https://opensso.dk/oidc/authorize
  ?response_type=code
  &client_id=DIT_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fapp.example.dk%2Fcallback
  &scope=openid%20profile
  &state=TILFAELDIG_STATE
  &nonce=TILFAELDIG_NONCE
  &code_challenge=BASE64URL(SHA256(code_verifier))
  &code_challenge_method=S256

Brugeren sendes tilbage til jeres redirect-URI med code og state. Kontrollér at state matcher, og byt koden til tokens:

curl -X POST https://opensso.dk/oidc/token \
  -u "DIT_CLIENT_ID:DIN_CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code=KODEN_FRA_CALLBACK \
  -d redirect_uri=https://app.example.dk/callback \
  -d code_verifier="$CODE_VERIFIER"
{
  "token_type": "Bearer",
  "access_token": "...",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9..."
}

Verificér id_token (signatur mod JWKS, iss, aud, exp og at nonce er den, I sendte).

Eksempel 2: PHP (jumbojett/openid-connect-php)

composer require jumbojett/openid-connect-php

<?php
require __DIR__.'/vendor/autoload.php';

use Jumbojett\OpenIDConnectClient;

session_start();

$oidc = new OpenIDConnectClient('https://opensso.dk', 'DIT_CLIENT_ID', 'DIN_CLIENT_SECRET');
$oidc->setRedirectURL('https://app.example.dk/callback');
$oidc->setCodeChallengeMethod('S256'); // PKCE er påkrævet
$oidc->addScope(['openid']);           // tilføj 'profile', 'unilogin', 'skole' hvis godkendt

$oidc->authenticate(); // sender brugeren til OpenSSO og håndterer callback

// id_token er verificeret (signatur, iss, aud, exp, nonce)
$sub = $oidc->getVerifiedClaims('sub');
$idToken = $oidc->getIdToken(); // gem til logout

Eksempel 3: Node.js (openid-client v5)

// npm install openid-client@5
import { Issuer, generators } from 'openid-client';

const issuer = await Issuer.discover('https://opensso.dk');

const client = new issuer.Client({
  client_id: 'DIT_CLIENT_ID',
  client_secret: 'DIN_CLIENT_SECRET',
  redirect_uris: ['https://app.example.dk/callback'],
  response_types: ['code'],
  // token_endpoint_auth_method er som standard client_secret_basic
});

// 1. Start login (gem verifier, state og nonce i brugerens session)
const codeVerifier = generators.codeVerifier();
const state = generators.state();
const nonce = generators.nonce();

const url = client.authorizationUrl({
  scope: 'openid profile',
  state,
  nonce,
  code_challenge: generators.codeChallenge(codeVerifier),
  code_challenge_method: 'S256',
});
// redirect brugeren til url

// 2. Callback (fx Express: req.query indeholder code og state)
const params = client.callbackParams(req);
const tokenSet = await client.callback(
  'https://app.example.dk/callback',
  params,
  { code_verifier: codeVerifier, state, nonce },
);
const claims = tokenSet.claims(); // verificeret id_token
console.log(claims.sub);

Logout

Send brugeren til /oidc/logout med id_token_hint, en registreret post_logout_redirect_uri og en state:

https://opensso.dk/oidc/logout
  ?id_token_hint=ID_TOKEN
  &post_logout_redirect_uri=https%3A%2F%2Fapp.example.dk%2Flogget-ud
  &state=TILFAELDIG_STATE

Med jumbojett i PHP:

$oidc->signOut($idToken, 'https://app.example.dk/logget-ud');

Fejl

Ældre alternativ: simpelt JWT-handoff

Det eksisterende WordPress-plugin bruger et enklere handoff: brugeren sendes til en start-URL hos OpenSSO og tilbage til jeres side med et JWT signeret med HS256 og en delt hemmelighed. Tokenet er gyldigt i 60 sekunder, og jti er et engangs-id, som modtageren skal afvise ved genbrug. Claims (felter uden værdi er null):

{
  "iss": "https://opensso.dk",
  "jti": "unikt-id-pr-token",
  "aud": "jeres-integrations-token",
  "iat": 1700000000,
  "exp": 1700000060,
  "sub": "...", "uniid": "...", "institution_ids": [...],
  "aktoer_gruppe": "...", "has_license": true
}

Til nye integrationer anbefaler vi OpenID Connect ovenfor.

Signeret videresendelse til tjenester uden OIDC

Kræver en tredjepart ikke OIDC, men et signeret link med et anonymt bruger-id, kan OpenSSO oversætte jeres bruger-id til et tilfældigt, stabilt pseudonym og sende browseren videre. Jeres side sender brugeren til GET /videresend/{integration_token}?userID=…&timestamp=…&hash=…, hvor hash er hex(HMAC-SHA256(delt hemmelighed, "{userID}&{timestamp}")). Linket er gyldigt i 120 sekunder og kan kun bruges én gang. OpenSSO videresender til tjenesten med pseudonymet, et nyt timestamp og en ny signatur - jeres bruger-id forlader aldrig OpenSSO.

Ofte stillede spørgsmål

Skal vi have en aftale med STIL?

Nej. I får jeres integration hos OpenSSO, og vi står for forbindelsen til Unilogin hos STIL. I behøver ikke selv være tilsluttet STIL eller have en dataaftale for at bruge OpenID Connect-integrationen.

Hvad koster det?

Se vores prisside eller kontakt os, så finder vi den rigtige løsning til jeres behov. Se priser.

Kan vi teste, før vi går i produktion?

Ja. Vi opretter en testintegration mod STILs testmiljø, så I kan afprøve hele loginflowet med testbrugere, før I skifter til produktion.

Brug for hjælp?

Skriv til info@opensso.dk eller kontakt Torben Langelund Sørensen direkte på torben@opensso.dk / +45 2750 7430.

Vi bruger kun statistik-cookies, hvis du accepterer det - læs mere i vores cookiepolitik.