For app vendors

Adding "Sign in with EduSpaze" to your app

Adding "Sign in with EduSpaze" to your app

For a vendor — Zoala's Laravel staff app and React pupil app are the example — this is all it takes for pupils and staff to arrive from EduSpaze already signed in. It is standard OpenID Connect, so your existing libraries do the work. Contract: §11 of the vendor integration guide.

1. Register

Startup portal → Integration → Sign in with EduSpaze:

  • Redirect address: https://app.zoala.co/auth/eduspaze/callback
  • Sign-in start address: https://app.zoala.co/auth/eduspaze

Copy the client ID and secret into your server's environment: EDUSPAZE_CLIENT_ID, EDUSPAZE_CLIENT_SECRET.

2. Staff web app (Laravel + Socialite)

Use the community OpenID Connect provider for Socialite (socialiteproviders/manager with a generic OIDC provider), pointed at discovery:

// config/services.php
'eduspaze' => [
    'base_url'      => 'https://www.eduspazeconnect.com',   // discovery: /.well-known/openid-configuration
    'client_id'     => env('EDUSPAZE_CLIENT_ID'),
    'client_secret' => env('EDUSPAZE_CLIENT_SECRET'),
    'redirect'      => 'https://app.zoala.co/auth/eduspaze/callback',
],

// routes/web.php
Route::get('/auth/eduspaze', fn () => Socialite::driver('eduspaze')->scopes(['openid', 'email', 'profile'])->redirect());

Route::get('/auth/eduspaze/callback', function () {
    $who  = Socialite::driver('eduspaze')->user();          // verifies the ID token
    $raw  = $who->getRaw();                                // tenant, subject, role, preferred_username …
    $user = User::where('email', $raw['preferred_username'] ?? $who->getEmail())->firstOrFail();
    Auth::login($user);
    request()->session()->regenerate();
    return $raw['role'] === 'student'
        ? redirect()->away(pupilAppHandOff($user))       // see 3
        : redirect('/app/dashboard/school/overview');
});

3. Pupil app (React)

The pupil app keeps its sign-in in the browser on its own site, so the server hands it over once, the same way the password sign-in does:

function pupilAppHandOff(User $user): string {
    $code = Str::random(48);
    Cache::put('eduspaze-handoff:'.hash('sha256', $code), $user->id, now()->addMinute());
    return 'https://www.student.zoala.co/eduspaze?code='.$code;
}
// POST /api/app/mobile/login/eduspaze {code} → the same answer as /api/app/mobile/login (Cache::pull: single use)
// <Route path="/eduspaze" element={<EduSpazeLogin />} /> — exchanges the code, stores the token, opens /home

(The same small page as in Building the one-time sign-in link, §2b.)

4. Try it

Sign in to EduSpaze as a pupil who has Zoala, open Zoala from the launcher: EduSpaze sends you to /auth/eduspaze, Zoala sends you to EduSpaze, EduSpaze sends you straight back with a code, and you land on Zoala's home page signed in.

Checklist

  • Verify the ID token (your library does): issuer https://www.eduspazeconnect.com, audience your client ID, RS256 keys from the JWKS.
  • Match by preferred_username; never create accounts from a claim you didn't expect.
  • Keep the client secret on the server. Make a new one in the portal if it ever leaks — the old one stops working at once.

Source: docs/integration/eduspaze-sign-in-guide.md in the repository. Last updated with the code it describes.