authed; } public function hasError(): bool { return $this->error !== ''; } public function getError(): string { return $this->error; } public function getMethod(): string { return $this->method; } public function isBasicAuth(): bool { return strcasecmp('basic', $this->method) === 0; } public function isBearerAuth(): bool { return strcasecmp('bearer', $this->method) === 0; } public function isMisuzuAuth(): bool { return strcasecmp('misuzu', $this->method) === 0; } public function getType(): string { return $this->type; } public function isRealUser(): bool { return strcasecmp('user', $this->type) === 0; } public function isAppUser(): bool { return strcasecmp('app', $this->type) === 0; } public function isUser(): bool { return $this->isRealUser() || $this->isAppUser(); } public function isPublicApp(): bool { return strcasecmp('pubapp', $this->type) === 0; } public function isConfidentialApp(): bool { return strcasecmp('confapp', $this->type) === 0; } public function isApp(): bool { return $this->isPublicApp() || $this->isConfidentialApp(); } public function getAppId(): string { return $this->appId; } public function getUserId(): string { return $this->userId; } public function hasWildcardScope(): bool { return $this->scope === null; } public function getScopeString(): string { return $this->scope === null ? '' : implode(' ', $this->scope); } public function getScopeArray(): array { return $this->scope ?? []; } public function hasScope(string $scope): bool { return $this->scope === null || in_array($scope, $this->scope); } public function getExpiresAt(): int { return $this->expires ?? PHP_INT_MAX; } public function hasExpired(): bool { return $this->expires !== null && $this->expires <= time(); } private function handleRpcResponse(mixed $info): void { if(!is_array($info)) { $this->error = json_encode($info); return; } if(array_key_exists('method', $info) && is_string($info['method'])) $this->method = $info['method']; if(array_key_exists('error', $info) && is_string($info['error'])) $this->error = $info['error']; if(array_key_exists('type', $info) && is_string($info['type'])) $this->type = $info['type']; if(array_key_exists('user', $info) && is_string($info['user'])) $this->userId = $info['user']; if(array_key_exists('app', $info) && is_string($info['app'])) $this->appId = $info['app']; if(array_key_exists('scope', $info) && is_array($info['scope'])) $this->scope = $info['scope']; if(array_key_exists('expires', $info) && is_array($info['expires'])) $this->expires = $info['expires']; } public function dump(): array { return [ 'authed' => $this->authed, 'error' => $this->error, 'method' => $this->method, 'type' => $this->type, 'user' => $this->userId, 'app' => $this->appId, 'scope' => $this->scope, 'expires' => $this->expires, ]; } private function attemptBasicAppAuthInternal(string $remoteAddr, string $clientId, string $clientSecret = ''): void { try { $this->handleRpcResponse($this->rpcClient->procedure('hanyuu:oauth2:attemptAppAuth', [ 'remoteAddr' => $remoteAddr, 'clientId' => $clientId, 'clientSecret' => $clientSecret, ])); } catch(RuntimeException $ex) { $this->error = 'rpc'; } } public function attemptBasicAppAuth(string $remoteAddr, string $clientId, string $clientSecret = ''): void { if($this->authed) return; $this->authed = true; $this->attemptBasicAppAuthInternal($remoteAddr, $clientId, $clientSecret); } public function basicAppAuthMiddleware($response, $request): void { if($this->authed) return; $header = explode(' ', (string)$request->getHeaderLine('Authorization'), 2); if(strcasecmp('basic', $header[0]) !== 0) return; $this->authed = true; $this->method = $header[0]; $parts = base64_decode($header[1] ?? ''); if($parts === false) { $this->error = 'format'; return; } $parts = explode(':', $parts, 2); $this->attemptBasicAppAuthInternal( (string)filter_input(INPUT_SERVER, 'REMOTE_ADDR'), $parts[0], $parts[1] ?? '' ); } private function attemptBearerTokenAuthInternal(string $remoteAddr, string $bearerToken): void { try { $this->handleRpcResponse($this->rpcClient->procedure( 'hanyuu:oauth2:attemptBearerAuth', ['remoteAddr' => $remoteAddr, 'token' => $bearerToken] )); } catch(RuntimeException $ex) { $this->error = 'rpc'; } } public function attemptBearerTokenAuth(string $remoteAddr, string $bearerToken): void { if($this->authed) return; $this->authed = true; $this->attemptBearerTokenAuthInternal($remoteAddr, $bearerToken); } public function bearerTokenAuthMiddleware($response, $request): void { if($this->authed) return; $header = explode(' ', (string)$request->getHeaderLine('Authorization'), 2); if(strcasecmp('bearer', $header[0]) !== 0) return; $this->authed = true; $this->method = $header[0]; $bearerToken = trim($header[1] ?? ''); if($bearerToken === '') { $this->error = 'format'; return; } $this->attemptBearerTokenAuthInternal( (string)filter_input(INPUT_SERVER, 'REMOTE_ADDR'), $bearerToken ); } private function attemptMisuzuTokenAuthInternal(string $remoteAddr, string $misuzuToken): void { try { $this->handleRpcResponse($this->rpcClient->procedure( 'misuzu:auth:attemptMisuzuAuth', ['remoteAddr' => $remoteAddr, 'token' => $misuzuToken] )); } catch(RuntimeException $ex) { $this->error = 'rpc'; } } public function attemptMisuzuTokenAuth(string $remoteAddr, string $misuzuToken): void { if($this->authed) return; $this->authed = true; $this->attemptMisuzuTokenAuthInternal($remoteAddr, $misuzuToken); } public function misuzuTokenAuthMiddleware($response, $request) { if($this->authed) return; $header = explode(' ', (string)$request->getHeaderLine('Authorization'), 2); if(strcasecmp('misuzu', $header[0]) !== 0) return; $this->authed = true; $this->method = $header[0]; $misuzuToken = trim($header[1] ?? ''); if($misuzuToken === '') { $this->error = 'format'; return; } $this->attemptMisuzuTokenAuthInternal( (string)filter_input(INPUT_SERVER, 'REMOTE_ADDR'), $misuzuToken ); } }