$event Event * @return void */ public function beforeFilter(EventInterface $event): void { parent::beforeFilter($event); $this->Authentication->allowUnauthenticated(['login']); } /** * POST /api/auth/login - exchange email + password for a JWT. * * @return \Cake\Http\Response */ public function login(): Response { $this->request->allowMethod(['post']); // A token is issued only for email/password credentials, never in exchange for another token. $result = $this->Authentication->getResult(); $provider = $this->Authentication->getAuthenticationService()->getAuthenticationProvider(); if ($result === null || !$result->isValid() || !$provider instanceof FormAuthenticator) { throw new UnauthorizedException('Invalid email or password.'); } /** @var \App\Model\Entity\User $user */ $user = $result->getData(); $user->last_login = DateTime::now(); $this->fetchTable('Users')->saveOrFail($user); $ttl = (int)Configure::read('Jwt.ttl', 3600); $now = time(); $token = JWT::encode([ 'iss' => 'testRestApi', 'sub' => $user->id, 'role' => $user->role, 'iat' => $now, 'exp' => $now + $ttl, ], (string)Configure::read('Jwt.secret'), 'HS256'); return $this->respond([ 'token' => $token, 'token_type' => 'Bearer', 'expires_in' => $ttl, 'user' => $user, ]); } /** * GET /api/auth/me - current user's profile. * PUT|PATCH /api/auth/me - update own profile (name, email, password). * * @return \Cake\Http\Response */ public function me(): Response { $this->request->allowMethod(['get', 'put', 'patch']); $usersTable = $this->fetchTable('Users'); $user = $usersTable->get($this->currentUser()->id); if ($this->request->is('get')) { return $this->respond($user); } $user = $usersTable->patchEntity($user, (array)$this->request->getData(), [ 'fields' => ['email', 'password', 'first_name', 'last_name'], ]); if (!$usersTable->save($user)) { return $this->respondValidationError($user); } return $this->respond($user); } }