Skip to content

Authenticatie, sessie en heartbeat

De monitor-app (monitor.persmonitor.nl) logt gebruikers in via de GraphQL API. Je krijgt een access token (15 min) en een refresh token (7 dagen). De heartbeat gebruikt de refresh token om periodiek nieuwe tokens aan te vragen.


GraphQL API (graphql.persmonitor.nl) — gebruik altijd de Authorization-header:

Authorization: Bearer <token>

De GraphQL API gebruikt geen cookies. Stuur bij elke request de token mee in bovenstaande header.

Twee soorten tokens:

  • API-token — voor login en register. Heeft scopes (auth:login, auth:register). Maak aan met php artisan api-token:create. Geen user-koppeling.
  • Session-token — access token na login. Krijg je via login of refreshToken. Gebruik voor alle andere operaties.

Monitor-app (monitor.persmonitor.nl) — in de browser:

  • Bij requests naar de monitor (bijv. /heartbeat, /dashboard) stuurt de browser automatisch de session-cookie en de token-cookie mee (zelfde domein).
  • Voor fetch in JavaScript: gebruik credentials: 'same-origin' zodat cookies worden meegestuurd.
  • De token-cookie heet standaard persmonitor_token (config: PERSMONITOR_AUTH_COOKIE).

Externe client met cookie — als je de token in een cookie bewaart en handmatig requests doet:

Cookie: persmonitor_token=<token>

Of combineer met de Authorization-header (de monitor-middleware leest beide; GraphQL API gebruikt alleen de header).


Overzicht

OnderdeelRol
GraphQL APILogin (access + refresh token), logout, sessionValid, me, refreshToken. Access token: 15 min. Refresh token: 7 dagen.
Laravel-session (monitor)Bewaart persmonitor_token, persmonitor_refresh_token, persmonitor_user. Verloopt na SESSION_LIFETIME minuten inactiviteit.
Token-cookies (monitor)persmonitor_token (access) + persmonitor_refresh_token (refresh). Beide httpOnly. Overleven Laravel-session.
Heartbeat (monitor)Elke 1 min: controleert session, vraagt met refresh token nieuwe tokens aan (refreshToken), werkt cookies bij.

Controleren of iemand ingelogd is

Stuur de token mee in de header Authorization: Bearer &lt;token&gt; en doe een van de volgende:

Optie 1: snelle check — query sessionValid
Geeft true of false. Geen user-gegevens, wel het snelst.

graphql
query SessionValid {
  sessionValid
}

Optie 2: check + user-gegevens — query me
Geeft de huidige gebruiker; zonder of met ongeldige token is me gewoon null.

graphql
query Me {
  me {
    uuid
    displayName
    email { address verified }
  }
}

In je client: als sessionValid false is of me null, behandel de gebruiker als uitgelogd (bijv. redirect naar login).

Zie Queries → sessionValid & me voor de volledige velden.


Token bijwerken (refresh token)

Om de sessie te verlengen zonder opnieuw in te loggen: roep de mutatie refreshToken aan met de refresh token in de header. De API geeft nieuwe access + refresh tokens terug. De oude refresh token wordt ongeldig (rotation).

graphql
mutation RefreshToken {
  refreshToken {
    accessToken
    refreshToken
  }
}
  • Header: Authorization: Bearer &lt;jouw-refresh-token&gt;
  • Response: { accessToken, refreshToken }. Vervang beide in je app.

Waarom refresh token? De access token gaat bij elke API-call mee; als die lekt, kan een aanvaller maximaal 15 min misbruik maken. De refresh token wordt alleen bij refreshToken gebruikt; die lekt niet bij normale API-calls. Bij diefstal van de refresh token: session binding (alleen User-Agent, niet IP) beperkt misbruik.

Session binding: De API controleert of de zelfde browser wordt gebruikt (User-Agent). IP wordt niet gecontroleerd, zodat je niet uitlogt bij WiFi/VPN/trein. Zet AUTH_SESSION_BINDING=false om ook deze check uit te schakelen.

Zie Mutations → refreshToken voor details.


API-token (voor login/register)

Login en register vereisen een API-token met de juiste scope. Zonder token krijg je een fout.

Eerste keer (nieuwe installatie):

bash
php artisan neo4j:indexes && php artisan persmonitor:setup

Maakt admin-account ([email protected]), organisatie, environment en API-token aan. De output bevat wachtwoord en token.

Handmatig token aanmaken:

bash
php artisan api-token:create --name=Monitor --scopes=auth:login,auth:register

Bewaar de output; die wordt niet opnieuw getoond. Geef de token aan de monitor-app via PERSMONITOR_API_TOKEN in .env.

  • Environment: --environment=&lt;uuid&gt; voor tokens uit het CMS (verplicht daar).
  • Verloop: --expires=2026-12-31 of weglaten = nooit verlopen.

Admin: Gebruikers met isAdmin: true kunnen bij alle environments (handig voor support). Het setup-commando maakt een admin-account aan.

Scopes:

  • auth:login — mag login aanroepen
  • auth:register — mag register aanroepen
  • auth:* — beide

Login

  1. Gebruiker stuurt e-mail + wachtwoord naar POST /login (monitor).
  2. Monitor roept GraphQL login-mutatie aan (met API-token in header).
  3. Bij succes:
    • Session: persmonitor_token, persmonitor_refresh_token en persmonitor_user worden in de Laravel-session gezet.
    • Cookies: Beide tokens in httpOnly-cookies (persmonitor_token, persmonitor_refresh_token), lifetime volgens config.
  4. Redirect naar /dashboard.

De GraphQL API retourneert bij login een access token en refresh token. De monitor slaat beide op; bij GraphQL-requests stuurt hij de access token mee.


Verlopen Laravel-session herstellen

Als de Laravel-session verlopen is (bijv. na 120 min zonder request), maar de cookies bevatten nog geldige tokens:

  1. Gebruiker opent een beveiligde pagina (of de heartbeat draait).
  2. RequirePersmonitorAuth-middleware: ziet geen tokens in session, wel in cookies.
  3. GraphQL sessionValid met access token uit cookie. Als ongeldig → cookies wissen, redirect naar /login.
  4. GraphQL me met die token → user ophalen.
  5. Session vullen met access token, refresh token + user; request gaat door.

De gebruiker hoeft dan niet opnieuw in te loggen.


Heartbeat

Op elke ingelogde pagina (monitor, layout met $user) draait in de browser elke minuut een heartbeat:

  1. GET /heartbeat (met session-cookie).
  2. Server (heartbeat-route):
    • Access token + refresh token uit session/cookie halen.
    • GraphQL sessionValid (met access token): als ongeldig → 401, cookies leegmaken.
    • GraphQL refreshToken (met refresh token): nieuwe access + refresh tokens ophalen; oude refresh token wordt ongeldig (rotation).
    • GraphQL me met de nieuwe access token (user voor session).
    • Session + cookies bijwerken met beide nieuwe tokens.
  3. Frontend: Bij 401 of success: false:
    • Eerste keer: één pagina-herlading (andere tab kan token hebben vernieuwd).
    • Tweede keer: modal "Niet meer ingelogd" met knoppen "Later" (blijven) en "Naar inloggen" (redirect naar /login). Zo verlies je geen onopgeslagen wijzigingen.

Gevolg: zolang een tab open is, wordt de sessie elke minuut verlengd (nieuwe token, opnieuw 14 dagen geldig) en blijven session + cookie in sync.


Sessies ophalen en beheren

Elke login en elke refreshToken wordt vastgelegd als een AuthSession in Neo4j:

  • tokenHash (SHA-256, nooit de plain token)
  • expiresAt, activityExpiresAt, lastActivityAt
  • ipAddress, userAgent
  • Relatie naar het huidige environment ((AuthSession)-[:CURRENT_ENVIRONMENT]->(Environment))

Je eigen sessies ophalen

Gebruik de query mySessions met je token in de header Authorization: Bearer &lt;token&gt;:

graphql
query MySessions {
  mySessions {
    uuid
    ipAddress
    userAgent
    lastActivityAt
    expiresAt
    isCurrent
  }
}

Je moet ingelogd zijn (geldige token) om je sessies te zien. De response bevat alle actieve sessies van jouw account, met isCurrent: true voor de sessie van het token dat je nu gebruikt.

Sessies op afstand beëindigen

  • deleteSession(uuid: String!) — beëindig één specifieke sessie (bijv. een verdacht apparaat).
  • deleteOtherSessions — beëindig alle sessies behalve de huidige (handig na “log overal uit”).

De monitor-app gebruikt deze operaties in Instellingen → Sessies.


Environment per sessie

Het gekozen environment is geen Laravel-session-veld meer maar onderdeel van de AuthSession. Twee operaties horen bij deze flow:

  • currentEnvironment – geeft het environment terug dat aan de huidige token hangt.
  • setCurrentEnvironment(environmentUuid: String!) – valideert of de gebruiker toegang heeft en koppelt dat environment aan de sessie.

De UI vraagt bij het renderen simpelweg currentEnvironment op; bij een wissel wordt setCurrentEnvironment aangeroepen.

Default environment (eerste keer)

Als je net bent ingelogd en de sessie nog geen environment heeft gekozen, bepaalt de API automatisch een default:

  1. De API haalt het eerste environment op waar je toegang toe hebt (HAS_MEMBER).
  2. Sortering: alfabetisch op organisatienaam, daarna environmentnaam (org.name, env.name).
  3. Dat environment wordt aan je sessie gekoppeld en teruggegeven.

Je wordt dus standaard in het eerste environment (A–Z) gezet waar je lid van bent.


Wanneer moet de gebruiker opnieuw inloggen?

  • Niet wanneer alleen de Laravel-session verlopen is → herstel via cookie + GraphQL (sessionValid + me).
  • Wel wanneer:
    • De gebruiker uitlogt (GraphQL logout maakt token ongeldig).
    • Een admin iemand uitlogt (token wordt aan de GraphQL-kant verwijderd).
    • De token aan de GraphQL-kant verlopen is (geen heartbeat meer, token TTL verstreken).
    • Heartbeat krijgt 401 → na één herlading toont de frontend een modal; gebruiker kan "Naar inloggen" kiezen.

Logout

  1. POST /logout (monitor, form met CSRF).
  2. Token uit session of cookie; GraphQL logout aanroepen (token ongeldig maken).
  3. Laravel-session invalideren.
  4. Auth-cookie wissen (expire in het verleden).
  5. Redirect naar /.

Configuratie monitor (.env)

VariabeleBetekenis
PERSMONITOR_GRAPHQL_URLGraphQL-endpoint (bijv. https://graphql.persmonitor.nl/graphql).
PERSMONITOR_AUTH_COOKIENaam van de token-cookie (default: persmonitor_token).
PERSMONITOR_AUTH_COOKIE_LIFETIMECookie-lifetime in minuten (default: 20160 = 14 dagen).
SESSION_DRIVERGebruik redis (of database) in productie i.v.m. meerdere tabbladen.
SESSION_LIFETIMEMinuten inactiviteit tot Laravel-session verloopt (heartbeat verlengt dit).

GraphQL-operaties die de monitor gebruikt

  • login – inloggen, retourneert token + user. Geen cookie van de API; monitor slaat token op.
  • logout – token ongeldig maken. Header: Authorization: Bearer &lt;token&gt;.
  • sessionValid – is de access token nog geldig? Header: Authorization: Bearer &lt;token&gt;.
  • me – huidige gebruiker. Header: Authorization: Bearer &lt;token&gt;.
  • refreshToken – nieuwe access + refresh tokens. Header: Authorization: Bearer &lt;refresh_token&gt;. Oude refresh token wordt ongeldig (rotation).

Zie Mutations en Queries voor de exacte voorbeelden.


Debuggen van RequirePersmonitorAuth

Lokaal kun je ?debug_auth=1 toevoegen aan een URL om te zien waarom iemand wel/geen toegang krijgt. Voorbeeld:

http://localhost:8741/dashboard?debug_auth=1

Als APP_DEBUG=true krijg je dan een JSON-respons met o.a.:

  • tokenSource (session, cookie of none)
  • Resultaat van sessionValid en me
  • reason (bijv. no_token, session_valid_failed)

Handig om snel te zien of de token in session/cookie zit of waarom de middleware iemand naar /login stuurt.