()
| 56 | } |
| 57 | |
| 58 | export function getIdentityClient(): IdentityClient { |
| 59 | return { |
| 60 | async getOauthProfileFromApiKey({ |
| 61 | apiKey, |
| 62 | accountUuid, |
| 63 | betaHeader, |
| 64 | timeout, |
| 65 | }) { |
| 66 | // Legacy path name retained for contract compatibility. |
| 67 | const endpoint = buildNoumenaPlatformUrl('/api/claude_cli_profile') |
| 68 | const response = await axios.get<OAuthProfileResponse>(endpoint, { |
| 69 | headers: { |
| 70 | 'x-api-key': apiKey, |
| 71 | 'anthropic-beta': betaHeader, |
| 72 | }, |
| 73 | params: { |
| 74 | account_uuid: accountUuid, |
| 75 | }, |
| 76 | timeout, |
| 77 | }) |
| 78 | return response.data |
| 79 | }, |
| 80 | |
| 81 | async getOauthProfileFromOauthToken({ accessToken, timeout }) { |
| 82 | const endpoint = buildNoumenaPlatformUrl('/api/oauth/profile') |
| 83 | const response = await axios.get<OAuthProfileResponse>(endpoint, { |
| 84 | headers: { |
| 85 | Authorization: `Bearer ${accessToken}`, |
| 86 | 'Content-Type': 'application/json', |
| 87 | }, |
| 88 | timeout, |
| 89 | }) |
| 90 | return response.data |
| 91 | }, |
| 92 | |
| 93 | async exchangeCodeForTokens({ requestBody, timeout }) { |
| 94 | // Noumena's authorization_code grant currently requires a form-encoded |
| 95 | // body even though refresh_token accepts JSON. |
| 96 | const response = await axios.post<OAuthTokenExchangeResponse>( |
| 97 | getOauthTokenUrl(), |
| 98 | new URLSearchParams( |
| 99 | Object.entries(requestBody).map(([key, value]) => [key, String(value)]), |
| 100 | ), |
| 101 | { |
| 102 | headers: { |
| 103 | 'Content-Type': 'application/x-www-form-urlencoded', |
| 104 | }, |
| 105 | timeout, |
| 106 | }, |
| 107 | ) |
| 108 | return response.data |
| 109 | }, |
| 110 | |
| 111 | async refreshOAuthToken({ requestBody, timeout }) { |
| 112 | const response = await axios.post<OAuthTokenExchangeResponse>( |
| 113 | getOauthTokenUrl(), |
| 114 | requestBody, |
| 115 | { |
no outgoing calls
no test coverage detected