(origin: string)
| 151 | }; |
| 152 | |
| 153 | export const discoverCliLogin = async (origin: string): Promise<CliLoginDiscovery> => { |
| 154 | let response: Response; |
| 155 | try { |
| 156 | response = await fetch(cliLoginUrl(origin), { headers: { accept: "application/json" } }); |
| 157 | } catch (cause) { |
| 158 | throw new DeviceLoginError( |
| 159 | `Could not reach ${origin} to start login: ${cause instanceof Error ? cause.message : String(cause)}`, |
| 160 | ); |
| 161 | } |
| 162 | if (!response.ok) { |
| 163 | throw new DeviceLoginError( |
| 164 | `${origin} does not support CLI login (GET /api/auth/cli-login returned ${response.status}). ` + |
| 165 | "It may be an older server, or a local/unauthenticated server that needs no login.", |
| 166 | ); |
| 167 | } |
| 168 | const body = await readJson(response); |
| 169 | const deviceAuthorizationEndpoint = asString(body.deviceAuthorizationEndpoint); |
| 170 | const tokenEndpoint = asString(body.tokenEndpoint); |
| 171 | const clientId = asString(body.clientId); |
| 172 | if (!deviceAuthorizationEndpoint || !tokenEndpoint || !clientId) { |
| 173 | throw new DeviceLoginError(`${origin} returned an incomplete CLI-login configuration.`); |
| 174 | } |
| 175 | return { |
| 176 | provider: asString(body.provider) ?? "unknown", |
| 177 | deviceAuthorizationEndpoint, |
| 178 | tokenEndpoint, |
| 179 | clientId, |
| 180 | scope: asString(body.scope), |
| 181 | requestFormat: asString(body.requestFormat) === "json" ? "json" : "form", |
| 182 | }; |
| 183 | }; |
| 184 | |
| 185 | export const requestDeviceCode = async (discovery: CliLoginDiscovery): Promise<DeviceCodeGrant> => { |
| 186 | const response = await post( |
no test coverage detected