aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-util/src/http-client/challenger.ts
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-04-19 10:27:26 -0300
committerSebastian <sebasjm@gmail.com>2024-04-19 10:27:30 -0300
commitfebcf372eecce2cdfed6dead0933d46b8797e2e1 (patch)
tree1372635a069ff867ff7cd6462e889a1c707ae34d /packages/taler-util/src/http-client/challenger.ts
parentef012d25af7a43cc22516ec80eaf6057b53aadd8 (diff)
downloadwallet-core-febcf372eecce2cdfed6dead0933d46b8797e2e1.tar.xz
challenger api
Diffstat (limited to 'packages/taler-util/src/http-client/challenger.ts')
-rw-r--r--packages/taler-util/src/http-client/challenger.ts260
1 files changed, 260 insertions, 0 deletions
diff --git a/packages/taler-util/src/http-client/challenger.ts b/packages/taler-util/src/http-client/challenger.ts
new file mode 100644
index 000000000..fa4214aa6
--- /dev/null
+++ b/packages/taler-util/src/http-client/challenger.ts
@@ -0,0 +1,260 @@
+import { HttpRequestLibrary, readTalerErrorResponse } from "../http-common.js";
+import { HttpStatusCode } from "../http-status-codes.js";
+import { createPlatformHttpLib } from "../http.js";
+import { LibtoolVersion } from "../libtool-version.js";
+import {
+ FailCasesByMethod,
+ ResultByMethod,
+ opKnownHttpFailure,
+ opSuccessFromHttp,
+ opUnknownFailure
+} from "../operation.js";
+import {
+ AccessToken,
+ codecForChallengeCreateResponse,
+ codecForChallengeSetupResponse,
+ codecForChallengeStatus,
+ codecForChallengerAuthResponse,
+ codecForChallengerInfoResponse,
+ codecForChallengerTermsOfServiceResponse
+} from "./types.js";
+import { makeBearerTokenAuthHeader } from "./utils.js";
+
+export type ChallengerResultByMethod<prop extends keyof ChallengerHttpClient> =
+ ResultByMethod<ChallengerHttpClient, prop>;
+export type ChallengerErrorsByMethod<prop extends keyof ChallengerHttpClient> =
+ FailCasesByMethod<ChallengerHttpClient, prop>;
+
+/**
+ */
+export class ChallengerHttpClient {
+ httpLib: HttpRequestLibrary;
+ public readonly PROTOCOL_VERSION = "1:0:0";
+
+ constructor(
+ readonly baseUrl: string,
+ httpClient?: HttpRequestLibrary,
+ ) {
+ this.httpLib = httpClient ?? createPlatformHttpLib();
+ }
+
+ isCompatible(version: string): boolean {
+ const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version);
+ return compare?.compatible ?? false;
+ }
+ /**
+ * https://docs.taler.net/core/api-challenger.html#get--config
+ *
+ */
+ async getConfig() {
+ const url = new URL(`config`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "GET",
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(
+ resp,
+ codecForChallengerTermsOfServiceResponse(),
+ );
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+ /**
+ * https://docs.taler.net/core/api-challenger.html#post--setup-$CLIENT_ID
+ *
+ */
+ async setup(clientId: string, token: AccessToken) {
+ const url = new URL(`setup/${clientId}`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "POST",
+ headers: {
+ Authorization: makeBearerTokenAuthHeader(token),
+ },
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForChallengeSetupResponse());
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
+ // LOGIN
+
+ /**
+ * https://docs.taler.net/core/api-challenger.html#post--authorize-$NONCE
+ *
+ */
+ 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);
+ url.searchParams.set("redirect_uri", redirectUri);
+ if (state) {
+ url.searchParams.set("state", state);
+ }
+ // url.searchParams.set("scope", "code");
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "POST",
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForChallengeStatus());
+ case HttpStatusCode.BadRequest:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotAcceptable:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.InternalServerError:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
+ // CHALLENGE
+
+ /**
+ * https://docs.taler.net/core/api-challenger.html#post--challenge-$NONCE
+ *
+ */
+ 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"
+ }
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForChallengeCreateResponse());
+ case HttpStatusCode.BadRequest:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotAcceptable:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.TooManyRequests:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.InternalServerError:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
+ // SOLVE
+
+ /**
+ * https://docs.taler.net/core/api-challenger.html#post--solve-$NONCE
+ *
+ */
+ async solve(nonce: string, body: Record<string, string>) {
+ const url = new URL(`solve/${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"
+ },
+ 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());
+ case HttpStatusCode.BadRequest:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotAcceptable:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.TooManyRequests:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.InternalServerError:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
+ // AUTH
+
+ /**
+ * https://docs.taler.net/core/api-challenger.html#post--token
+ *
+ */
+ async token(
+ client_id: string,
+ redirect_uri: string,
+ client_secret: AccessToken,
+ code: string,
+ ) {
+ const url = new URL(`token`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ },
+ body: new URLSearchParams(Object.entries({
+ client_id,
+ redirect_uri,
+ client_secret,
+ code,
+ grant_type: "authorization_code",
+ })).toString(),
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForChallengerAuthResponse());
+ case HttpStatusCode.Forbidden:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
+ // INFO
+
+ /**
+ * https://docs.taler.net/core/api-challenger.html#get--info
+ *
+ */
+ async info(token: AccessToken) {
+ const url = new URL(`info`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "GET",
+ headers: {
+ Authorization: makeBearerTokenAuthHeader(token),
+ },
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForChallengerInfoResponse());
+ case HttpStatusCode.Forbidden:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+}