Universal Login
What is Universal Login?
Universal Login is the hosted authentication interface operated by LuxID at https://login.luxid.lu (opens in a new tab).
It is the page your users see when they click "Sign in with LuxID" in your application. All credential entry, MFA prompts, account recovery flows, and consent screens happen on LuxID's infrastructure - never inside your application.
The flow is redirect-based: your application sends the user to the LuxID authorisation endpoint, LuxID authenticates them, and redirects back to your registered callback URI with an authorisation code. Your application never sees the user's password.
Why use it?
Using Universal Login is the strongly recommended approach for all LuxID Partners, and in most cases the only supported approach. Here is why.
Security by architecture. Credential entry happens on a domain your application has no access to (login.luxid.lu). This eliminates an entire class of vulnerabilities: keystroke logging in your application, man-in-the-middle password interception, and accidental logging of credentials. It also means that a security incident in your application cannot compromise LuxID credentials.
MFA without integration work. LuxID handles all MFA orchestration - prompting for OTP, TOTP, passkey assertion, or LuxTrust authentication based on the assurance level configured for your Application. You receive a completed assurance level in the acr claim; you do not need to implement any MFA logic.
Consistent user experience. Users learn one sign-in interface for all LuxID-enabled Applications. This reduces friction and support burden: a user who signs in to MyPost, RTL Play, and CFL services sees the same familiar LuxID login page each time.
Recovery paths included. Password reset, "resend activation email", and lockout recovery flows are built into Universal Login. You do not need to implement or host these flows.
Regulatory alignment. Redirect-based authentication with a hosted login page follows RFC 9700 OAuth 2.0 Security Best Current Practice (opens in a new tab), which explicitly recommends against embedding credential entry in third-party applications.
Developer impact
Token impact
Universal Login is the UI layer. It does not introduce any token claims of its own. The tokens you receive after a successful authentication are determined by the scopes you requested, the claims template configured for your Application, and the assurance level reached during the session.
What Universal Login does influence:
auth_time- the Unix timestamp of the authentication event on Universal Loginacr- the assurance level reached (determined by the MFA methods completed on Universal Login)amr- the array of authentication methods used (e.g.["pwd", "otp"])
Endpoints
| Purpose | URL |
|---|---|
| Authorisation (start login) | https://login.luxid.lu/mga/sps/oauth/oauth20/authorize |
| Token exchange | https://login.luxid.lu/mga/sps/oauth/oauth20/token |
| User-driven sign-out | https://login.luxid.lu/auth/logout?client_id=<client_id> (no OIDC end_session_endpoint) |
| Discovery | https://login.luxid.lu/.well-known/openid-configuration |
Silent re-authentication
If the user already has an active LuxID session cookie from a previous sign-in, Universal Login will not prompt them for credentials again. Instead, it will complete the authorisation flow silently and redirect back to your application with a fresh set of tokens.
This is standard OIDC session behaviour. The auth_time claim in the ID Token reflects the original authentication time, not the current redirect.
To check whether the user is still actively signed in without initiating a full redirect, use the prompt=none parameter:
GET https://login.luxid.lu/mga/sps/oauth/oauth20/authorize
?client_id=<your_client_id>
&response_type=code
&redirect_uri=<your_callback>
&scope=openid profile email
&prompt=none
&state=<state>
&nonce=<nonce>
If the session is active, you receive an authorisation code. If not, you receive an error=login_required response and should initiate a full interactive login.
Forcing a new login
To require a fresh authentication - for example, before a sensitive operation such as changing an email address - use prompt=login:
GET https://login.luxid.lu/mga/sps/oauth/oauth20/authorize
?client_id=<your_client_id>
&response_type=code
&redirect_uri=<your_callback>
&scope=openid profile email
&prompt=login
&state=<state>
&nonce=<nonce>
The user will be required to enter their credentials even if a valid session cookie exists.
Step-up authentication
To require a higher assurance level than the existing session provides - for example, requiring MFA for a financial transaction even though the user signed in with password only - use the acr_values parameter:
GET https://login.luxid.lu/mga/sps/oauth/oauth20/authorize
?client_id=<your_client_id>
&response_type=code
&redirect_uri=<your_callback>
&scope=openid profile email
&acr_values=urn:luxid:acr:level:substantial
&state=<state>
&nonce=<nonce>
The canonical acr_values form is urn:luxid:acr:level:low|substantial|high; the accepted request values are agreed per Partner during onboarding.
LuxID will prompt the user to complete the additional factor if the current session does not already satisfy the requested assurance level.
See Multi-factor authentication for the full list of acr_values.
Configuration
Branding
Each Application registered with LuxID can display its own name and logo on the Universal Login page. The user sees:
- Your application name (as registered in the Console)
- Your application logo (uploaded in the Console, displayed in a defined size)
- The LuxID wordmark and branding
The page layout, colours, typography, and interactive elements are controlled by LuxID and are not Partner-configurable. This ensures a consistent, trustworthy experience for users and is intentional: users can rely on the LuxID login page looking the same across all applications.
See UX and Branding: Universal Login UX for logo size specifications and the visual design guidelines.
Registered redirect URIs
Your Application must have one or more redirect URIs registered with LuxID. LuxID will only redirect to a registered URI after authentication. Any attempt to redirect to an unregistered URI will result in an error.
- Use HTTPS for all production redirect URIs
- Wildcards in redirect URIs are not supported; register each callback path explicitly
- Localhost (
http://localhost) is permitted for development and testing; it must not be used in production
Supported response types
Universal Login supports the following OIDC response types:
| Response type | Use case |
|---|---|
code | Standard authorisation code flow. Recommended for all server-side and SPA applications with PKCE. |
code id_token | Hybrid flow. Supported but not recommended for new integrations. |
The implicit flow (response_type=token or response_type=id_token) is not supported.
Example token claims after Universal Login
The following is an excerpt from a decoded ID Token after a user signs in via Universal Login with password and SMS OTP:
{
"iss": "https://login.luxid.lu",
"sub": "a3f2c1d8-9b4e-4a2f-8c1d-3e7b9a2f1c4d",
"aud": "your_client_id",
"iat": 1748476800,
"exp": 1748480400,
"auth_time": 1748476800,
"nonce": "n-0S6_WzA2Mj",
"acr": "urn:luxid:acr:level:substantial",
"amr": ["pwd", "otp"],
"email": "utilisateur@example.lu",
"email_verified": true,
"name": "Marie Dupont",
"given_name": "Marie",
"family_name": "Dupont"
}
The auth_time, acr, and amr fields are the primary signals of what happened during the Universal Login session.
Security and privacy notes
Phishing resistance. Because credential entry happens exclusively on login.luxid.lu, your application cannot accidentally (or maliciously) harvest passwords. Users who are trained to look for login.luxid.lu in the browser address bar before entering their credentials are significantly more resistant to phishing attacks that attempt to replicate the login page.
HTTPS enforced. Universal Login is only served over HTTPS with modern TLS. All redirect URIs in production must also use HTTPS.
State parameter. Always generate a cryptographically random state parameter and verify it on return to your redirect URI. This prevents cross-site request forgery (CSRF) attacks against your authorisation flow.
Nonce parameter. Always include a nonce in your authorisation request and verify that it appears unchanged in the returned ID Token. This prevents replay attacks.
Session cookies. The LuxID session cookie is set on the login.luxid.lu domain, not on your application domain. Your application does not have access to this cookie. Do not attempt to read or modify it.
LuxID sets X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'none'. Embedding will fail and the browser will block the page. This is intentional: it prevents clickjacking attacks.
Logout. Clearing your own application session does not end the LuxID session. LuxID does not expose an OIDC end_session_endpoint and does not implement RP-Initiated Logout - this is deliberate, because LuxID is the shared SSO authority for many Partners and one application should not be able to terminate sessions other Partners rely on. If the user explicitly wants to end their LuxID session as well, redirect them to:
GET https://login.luxid.lu/auth/logout?client_id=<your_client_id>
LuxID renders a logout confirmation screen that displays your application name (derived from the client_id); the user then chooses whether to end the LuxID session. If you do not do this, the user keeps a valid LuxID session cookie and may be silently re-authenticated on their next visit. See Session management for the full discussion.