/** * Handle JMAP authentication requests (POST) */ public function processPOST(Request $request, Response $response) { // decode JMAP request data $json_input = json_decode($request->getBodyAsString(), true); if ($json_input === null) { throw new ProcessorException(400, "Invalid JSON request body"); } $response->setHeader('Content-Type', 'application/json'); // initial auth request if (isset($json_input['username'])) { $this->ctrl->emit('jmap:auth:init', [['data' => $json_input, 'processor' => $this]]); $methods = []; foreach ($this->providers as $provider) { $methods = array_merge($methods, $provider->getAuthMethods()); } // start session and generate the loginId $this->session->start(); $this->session->set('Auth\\username', $json_input['username']); $result = ['methods' => array_unique($methods), 'loginId' => $this->session->key]; // add prompt phrase stored in config if ($prompt = App::getInstance()->get('Config')->get('auth.prompt')) { $result['prompt'] = $prompt; } $status = 200; $this->ctrl->emit('jmap:auth:more', [['result' => &$result, 'status' => &$status, 'processor' => $this]]); $response->setBody(json_encode($result)); $response->setStatus($status); } else { if (isset($json_input['loginId']) && isset($json_input['type'])) { $this->session->start($json_input['loginId']); // trigger event AFTER session has been initialized with the continuation token $this->ctrl->emit('jmap:auth:continue', [['data' => $json_input, 'processor' => $this]]); // validate token (which is the session key) ... if ($this->session->key !== $json_input['loginId'] || empty($this->session->get('Auth\\username'))) { $this->ctrl->logger->debug("Invalid session loginId provided", ['input' => $json_input, 'session' => $this->session->key, 'username' => $this->session->get('Auth\\username')]); $this->ctrl->emit('jmap:auth:restart', [['input' => $json_input, 'status' => 410, 'processor' => $this]]); $response->setStatus(410); // Restart authentication return; } // ...and get username from session $json_input['username'] = $this->session->get('Auth\\username'); $authenticated = false; foreach ($this->providers as $provider) { if (self::getAuthMethod($provider, $json_input['type'])) { try { $authenticated = $provider->authenticate($json_input); } catch (AuthenticationAbortedException $e) { break; } } if ($authenticated) { break; } } // authentication successful if ($authenticated && $authenticated instanceof AuthenticatedIdentity) { $authenticated->username = $json_input['username']; // replace session and generate the accessToken $this->session->regenerateId(true); $this->session->set('Auth\\authenticated', time()); $this->session->set('Auth\\identity', $authenticated); // send success response $this->sendAuthSuccess($response, $this->session->key); } else { // report authentication failure // TODO: create the same response as in initial auth request above $this->ctrl->emit('jmap:auth:failure', [['input' => $json_input, 'status' => 403, 'processor' => $this]]); $response->setStatus(403); } } else { $response->setStatus(401); } } }