diff options
author | Sebastian <sebasjm@gmail.com> | 2024-04-21 14:55:26 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2024-04-22 08:52:55 -0300 |
commit | eada01727571fe0aae632696baa97bc4ab6be521 (patch) | |
tree | 9feae9659e13a6f902144486175cbf98a201ae8c | |
parent | 96d0833d703a55e9a5e707dd464f85ad0c78ab0e (diff) | |
download | wallet-core-eada01727571fe0aae632696baa97bc4ab6be521.tar.xz |
fix challenger api
-rw-r--r-- | packages/taler-util/src/http-client/challenger.ts | 67 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/types.ts | 8 | ||||
-rw-r--r-- | packages/taler-util/src/http-common.ts | 41 | ||||
-rw-r--r-- | packages/taler-util/src/operation.ts | 5 |
4 files changed, 92 insertions, 29 deletions
diff --git a/packages/taler-util/src/http-client/challenger.ts b/packages/taler-util/src/http-client/challenger.ts index fa4214aa6..8d23ed273 100644 --- a/packages/taler-util/src/http-client/challenger.ts +++ b/packages/taler-util/src/http-client/challenger.ts @@ -4,10 +4,13 @@ import { createPlatformHttpLib } from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; import { FailCasesByMethod, + RedirectResult, ResultByMethod, + opFixedSuccess, + opKnownAlternativeFailure, opKnownHttpFailure, opSuccessFromHttp, - opUnknownFailure + opUnknownFailure, } from "../operation.js"; import { AccessToken, @@ -16,7 +19,8 @@ import { codecForChallengeStatus, codecForChallengerAuthResponse, codecForChallengerInfoResponse, - codecForChallengerTermsOfServiceResponse + codecForChallengerTermsOfServiceResponse, + codecForInvalidPinResponse, } from "./types.js"; import { makeBearerTokenAuthHeader } from "./utils.js"; @@ -91,7 +95,12 @@ export class ChallengerHttpClient { * https://docs.taler.net/core/api-challenger.html#post--authorize-$NONCE * */ - async login(nonce: string, clientId: string, redirectUri: string, state: string | undefined) { + async login( + nonce: string, + clientId: string, + redirectUri: string, + state: string | undefined, + ) { const url = new URL(`authorize/${nonce}`, this.baseUrl); url.searchParams.set("response_type", "code"); url.searchParams.set("client_id", clientId); @@ -127,17 +136,23 @@ export class ChallengerHttpClient { */ async challenge(nonce: string, body: Record<"email", string>) { const url = new URL(`challenge/${nonce}`, this.baseUrl); - + const resp = await this.httpLib.fetch(url.href, { method: "POST", body: new URLSearchParams(Object.entries(body)).toString(), headers: { - "Content-Type": "application/x-www-form-urlencoded" - } + "Content-Type": "application/x-www-form-urlencoded", + }, + redirect: "manual", }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForChallengeCreateResponse()); + case HttpStatusCode.Found: + const redirect = resp.headers.get("Location")!; + return opFixedSuccess<RedirectResult>({ + redirectURL: new URL(redirect), + }); case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: @@ -165,23 +180,25 @@ export class ChallengerHttpClient { method: "POST", body: new URLSearchParams(Object.entries(body)).toString(), headers: { - "Content-Type": "application/x-www-form-urlencoded" + "Content-Type": "application/x-www-form-urlencoded", }, redirect: "manual", }); switch (resp.status) { case HttpStatusCode.Found: - const redirect = resp.headers.get("Location")! - const uri = new URL(redirect) - const code = uri.searchParams.get("code")! - return { - type: "ok" as const, - body: { code } - } - // return opSuccessFromHttp(resp, codecForChallengeCreateResponse()); + const redirect = resp.headers.get("Location")!; + return opFixedSuccess<RedirectResult>({ + redirectURL: new URL(redirect), + }); case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: + case HttpStatusCode.Forbidden: + return opKnownAlternativeFailure( + resp, + resp.status, + codecForInvalidPinResponse(), + ); + case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotAcceptable: return opKnownHttpFailure(resp.status, resp); @@ -210,15 +227,17 @@ export class ChallengerHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - "Content-Type": "application/x-www-form-urlencoded" + "Content-Type": "application/x-www-form-urlencoded", }, - body: new URLSearchParams(Object.entries({ - client_id, - redirect_uri, - client_secret, - code, - grant_type: "authorization_code", - })).toString(), + body: new URLSearchParams( + Object.entries({ + client_id, + redirect_uri, + client_secret, + code, + grant_type: "authorization_code", + }), + ).toString(), }); switch (resp.status) { case HttpStatusCode.Ok: diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts index e12c2ed6b..13ef9a3e6 100644 --- a/packages/taler-util/src/http-client/types.ts +++ b/packages/taler-util/src/http-client/types.ts @@ -1533,7 +1533,7 @@ export const codecForChallengeCreateResponse = export const codecForInvalidPinResponse = (): Codec<ChallengerApi.InvalidPinResponse> => buildCodecForObject<ChallengerApi.InvalidPinResponse>() - .property("ec", codecForNumber()) + .property("ec", codecOptional(codecForNumber())) .property("hint", codecForAny()) .property("addresses_left", codecForNumber()) .property("pin_transmissions_left", codecForNumber()) @@ -5319,17 +5319,17 @@ export namespace ChallengerApi { // timestamp explaining when we would re-transmit the challenge the next // time (at the earliest) if requested by the user - next_tx_time: String; + next_tx_time: string; } export interface InvalidPinResponse { // numeric Taler error code, should be shown to indicate the error // compactly for reporting to developers - ec: Integer; + ec?: number; // human-readable Taler error code, should be shown for the user to // understand the error - hint: String; + hint: string; // how many times is the user still allowed to change the address; // if 0, the user should not be shown a link to jump to the diff --git a/packages/taler-util/src/http-common.ts b/packages/taler-util/src/http-common.ts index cc75debd5..d8cd36287 100644 --- a/packages/taler-util/src/http-common.ts +++ b/packages/taler-util/src/http-common.ts @@ -268,6 +268,47 @@ export async function readSuccessResponseJsonOrErrorCode<T>( }; } +export async function readResponseJsonOrErrorCode<T>( + httpResponse: HttpResponse, + codec: Codec<T>, +): Promise<{ isError: boolean; response: T }> { + let respJson; + try { + respJson = await httpResponse.json(); + } catch (e: any) { + throw TalerError.fromDetail( + TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, + { + requestUrl: httpResponse.requestUrl, + requestMethod: httpResponse.requestMethod, + httpStatusCode: httpResponse.status, + validationError: e.toString(), + }, + "Couldn't parse JSON format from response", + ); + } + let parsedResponse: T; + try { + parsedResponse = codec.decode(respJson); + } catch (e: any) { + throw TalerError.fromDetail( + TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, + { + requestUrl: httpResponse.requestUrl, + requestMethod: httpResponse.requestMethod, + httpStatusCode: httpResponse.status, + validationError: e.toString(), + }, + "Response invalid", + ); + } + return { + isError: !(httpResponse.status >= 200 && httpResponse.status < 300), + response: parsedResponse, + }; +} + + type HttpErrorDetails = { requestUrl: string; requestMethod: string; diff --git a/packages/taler-util/src/operation.ts b/packages/taler-util/src/operation.ts index 771f5860b..e2ab9d4e4 100644 --- a/packages/taler-util/src/operation.ts +++ b/packages/taler-util/src/operation.ts @@ -19,6 +19,7 @@ */ import { HttpResponse, + readResponseJsonOrErrorCode, readSuccessResponseJsonOrThrow, readTalerErrorResponse, } from "./http-common.js"; @@ -126,7 +127,7 @@ export async function opKnownAlternativeFailure<T extends HttpStatusCode, B>( s: T, codec: Codec<B>, ): Promise<OperationAlternative<T, B>> { - const body = await readSuccessResponseJsonOrThrow(resp, codec); + const body = (await readResponseJsonOrErrorCode(resp, codec)).response; return { type: "fail", case: s, body }; } @@ -193,3 +194,5 @@ export type FailCasesByMethod<TT extends object, p extends keyof TT> = Exclude< ResultByMethod<TT, p>, OperationOk<any> >; + +export type RedirectResult = { redirectURL: URL } |