getPathInfo() === '/login' && $request->headers->has('Authorization')) { return true; } return false; } /** * Performs authentication using the Nostr Authorization header. * * @param Request $request The HTTP request. * @return SelfValidatingPassport The authenticated passport. * @throws AuthenticationException If authentication fails (invalid header, expired, or invalid signature). */ public function authenticate(Request $request): SelfValidatingPassport { $authHeader = $request->headers->get('Authorization'); if (!str_starts_with($authHeader, 'Nostr ')) { throw new AuthenticationException('Invalid Authorization header'); } $eventStr = base64_decode(substr($authHeader, 6), true); if (false === $eventStr) { throw new AuthenticationException('Invalid Authorization header'); } try { $data = json_decode($eventStr, false, 512, \JSON_THROW_ON_ERROR); } catch (\JsonException) { throw new AuthenticationException('Invalid Authorization header'); } if (!\is_object($data) || !isset( $data->id, $data->pubkey, $data->created_at, $data->kind, $data->content, $data->sig )) { throw new AuthenticationException('Invalid Authorization header'); } if (!isset($data->tags) || !\is_array($data->tags)) { $data->tags = []; } $event = (new Event())->populate($data); if (time() > $event->getCreatedAt() + 60) { throw new AuthenticationException('Expired'); } $validity = (new SchnorrSignature())->verify( $event->getPublicKey(), $event->getSignature(), $event->getId() ); if (!$validity) { throw new AuthenticationException('Invalid Authorization header'); } $key = new Key(); return new SelfValidatingPassport( new UserBadge($key->convertPublicKeyToBech32($event->getPublicKey())) ); } /** * Handles successful authentication. * * @param Request $request The HTTP request. * @param TokenInterface $token The authenticated token. * @param string $firewallName The firewall name. * @return Response|null The response to return, or null to continue. */ public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { return new Response('Authentication Successful', 200); } /** * Handles failed authentication. * * @param Request $request The HTTP request. * @param AuthenticationException $exception The exception thrown during authentication. * @return Response|null The response to return, or null to continue. */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { return null; } /** * Indicates whether this authenticator is interactive. * * @return bool True if interactive. */ public function isInteractive(): bool { return true; } }