Acapadev

Capítulo 4

A Lógica de Callback (O Coração da Autenticação)

Após o utilizador ser validado no ACAPADEV ID, será reencaminhado de volta para a sua aplicação através da URL de Redirect que configurou.

Aqui está o código completo necessário para processar este regresso com segurança.

1. Adicionar as Rotas

No ficheiro routes/web.php da sua aplicação:

use Laravel\Socialite\Facades\Socialite;
use App\Http\Controllers\Auth\OAuthController;

// 1. Envia o utilizador para o ACAPADEV ID
Route::get('/auth/redirect', [OAuthController::class, 'redirect'])->name('oauth.redirect');

// 2. Recebe a resposta com o código de autorização
Route::get('/auth/callback', [OAuthController::class, 'callback'])->name('oauth.callback');

2. A Lógica Receção (Callback)

O ACAPADEV ID é agnóstico em relação ao seu backend. Crie um endpoint/rota que processa o Redirect e obtém o Token do utilizador. Abaixo encontra exemplos robustos em múltiplas tecnologias.

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;

class OAuthController extends Controller
{
    public function redirect()
    {
        return Socialite::driver('laravelpassport')->redirect();
    }

    public function callback()
    {
        try {
            // Extrair os dados do ACAPADEV ID
            $ssoUser = Socialite::driver('laravelpassport')->user();
            
            // Dica RBAC: O ACAPADEV ID envia o cargo atribuído
            $assignedRole = $ssoUser->user['app_role'] ?? null;

            // 1. Procurar por utilizador com este SSO ID
            $user = User::where('sso_id', $ssoUser->id)->first();

            if (!$user) {
                // 2. Se não encontrou pelo SSO ID, tenta pelo e-mail
                $user = User::where('email', $ssoUser->email)->first();

                if ($user) {
                    $user->update(['sso_id' => $ssoUser->id, 'avatar' => $ssoUser->avatar]);
                } else {
                    // 3. Regista totalmente novo!
                    $user = User::create([
                        'name'     => $ssoUser->name,
                        'email'    => $ssoUser->email,
                        'sso_id'   => $ssoUser->id,
                        'avatar'   => $ssoUser->avatar,
                        'password' => null, // O SSO gere a senha
                    ]);
                }
            }
            
            // Sincronizar cargo localmente
            if ($assignedRole === 'admin') {
                $user->is_admin = true;
                $user->save();
            }

            Auth::login($user, true);
            return redirect()->intended('/dashboard');

        } catch (\Exception $e) {
            return redirect('/login')->with('error', 'Falha na autenticação via ACAPADEV ID.');
        }
    }
}
const express = require('express');
const passport = require('passport');
const OAuth2Strategy = require('passport-oauth2').Strategy;
const User = require('./models/User');

const app = express();

passport.use('acapadev', new OAuth2Strategy({
    authorizationURL: 'https://id.acapadev.com/oauth/authorize',
    tokenURL: 'https://id.acapadev.com/oauth/token',
    clientID: process.env.ACAPADEV_CLIENT_ID,
    clientSecret: process.env.ACAPADEV_CLIENT_SECRET,
    callbackURL: 'https://sua-app.com/auth/callback'
},
async function(accessToken, refreshToken, profile, cb) {
    try {
        // Em Node.js precisará de fazer um GET extra a /api/user 
        // para buscar os detalhes do profile se o token não os incluir.
        const response = await fetch('https://id.acapadev.com/api/user', {
            headers: { 'Authorization': `Bearer ${accessToken}` }
        });
        const ssoUser = await response.json();
        
        let user = await User.findOne({ sso_id: ssoUser.id });
        if (!user) {
            user = await User.findOne({ email: ssoUser.email });
            if (user) {
                user.sso_id = ssoUser.id;
                await user.save();
            } else {
                user = await User.create({
                    name: ssoUser.name,
                    email: ssoUser.email,
                    sso_id: ssoUser.id
                });
            }
        }
        return cb(null, user);
    } catch (err) {
        return cb(err, null);
    }
}
));

app.get('/auth/redirect', passport.authenticate('acapadev'));

app.get('/auth/callback', 
    passport.authenticate('acapadev', { failureRedirect: '/login' }),
    function(req, res) {
        res.redirect('/dashboard');
    }
);

Dicas de Prevenção de Erros

  • Sempre confie no SSO: Se a conta foi revogada ou suspensa no Painel Super-Admin do ACAPADEV ID, a sua aplicação nem sequer recebe a resposta do Callback. O bloqueio é absoluto e imediato.
  • Session Hijacking: Ao iniciar sessão, é sempre recomendado regenerar o ID da sessão do PHP com $request->session()->regenerate(); após o Auth::login(). O nosso exemplo utiliza o motor interno de Auth do Laravel que já faz isto parcialmente, mas para extrema segurança, adicione esse regenerador.

Integração Concluída! 🎉

A sua aplicação já faz parte do ecossistema centralizado ACAPADEV.

Enter para selecionar ESC para sair