diff options
author | Sebastian <sebasjm@gmail.com> | 2024-04-19 10:27:26 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2024-04-19 10:27:30 -0300 |
commit | febcf372eecce2cdfed6dead0933d46b8797e2e1 (patch) | |
tree | 1372635a069ff867ff7cd6462e889a1c707ae34d /packages/taler-util/src/http-client/challenger.ts | |
parent | ef012d25af7a43cc22516ec80eaf6057b53aadd8 (diff) | |
download | wallet-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.ts | 260 |
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)); + } + } +} |