Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ async function someBrokerizeActions() {
createWebSocket: (url, protocol) => new WebSocket(url, protocol),
// basePath: 'https://api-preview.brokerize.com', // this is the default value
// basePathCryptoService: 'https://crypto-service-api.com' // the optional external crypto service
// acceptLanguage: 'de', // optional `Accept-Language` header for localized responses (e.g. legal terms).
// // may also be a function `() => 'de'` that is evaluated on every request,
// // so runtime language changes are picked up without recreating the client.
})

/* create a guest user. the result contains the user's tokens and be stored, e.g. in a cookie or session storage */
Expand Down
48 changes: 46 additions & 2 deletions src/apiCtx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,50 @@ export interface BrokerizeConfig {
* The AWS cognito configuration, if the application is supposed to be used with brokerize accounts.
*/
cognito?: CognitoConfig;
/**
* Optional value for the `Accept-Language` header sent with API requests. Use this to request
* localized backend responses (e.g. legal terms) in the language selected in your application,
* overriding the language the browser would send by default.
*
* Accepts a static value (e.g. `"de"`, `"en"`, `"de-DE"`) or a function that returns the current
* language. The function is evaluated on every request, so runtime language changes are picked up
* without recreating the client. Return `undefined`/`null` (or provide no value) to omit the
* header and let the runtime/browser default apply.
*/
acceptLanguage?:
| string
| (() => string | null | undefined | Promise<string | null | undefined>);
}

/**
* Resolves the configured `Accept-Language` value (static or via a getter function) to a string,
* or `undefined` if none is configured / the getter yields an empty value.
*/
export async function resolveAcceptLanguage(
cfg: BrokerizeConfig,
): Promise<string | undefined> {
const { acceptLanguage } = cfg;
const value =
typeof acceptLanguage === "function"
? await acceptLanguage()
: acceptLanguage;
return value || undefined;
}

/**
* Adds the configured `Accept-Language` header to the given headers object (mutating and returning
* it) if a language is configured. No-op otherwise. Used to apply the configured language uniformly
* to authorized and unauthenticated requests.
*/
export async function withAcceptLanguage(
cfg: BrokerizeConfig,
headers: Record<string, string>,
): Promise<Record<string, string>> {
const acceptLanguage = await resolveAcceptLanguage(cfg);
if (acceptLanguage) {
headers["Accept-Language"] = acceptLanguage;
}
return headers;
}

export type AuthContextConfiguration =
Expand Down Expand Up @@ -102,10 +146,10 @@ export function createAuth({
}
const response = await fetch(cfg.basePath + "/user/token", {
method: "POST",
headers: {
headers: await withAcceptLanguage(cfg, {
"x-brkrz-client-id": cfg.clientId,
"Content-Type": "application/x-www-form-urlencoded",
},
}),
// XXX some runtimes do not have URLSearchParams, so just produce the body in the old-fashioned way
body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(
guestAuthCfg.tokens.response.refreshToken,
Expand Down
18 changes: 12 additions & 6 deletions src/authorizedApiContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Subject } from "rxjs";
import { Auth, BrokerizeConfig, createConfiguration } from "./apiCtx";
import {
Auth,
BrokerizeConfig,
createConfiguration,
withAcceptLanguage,
} from "./apiCtx";
import { BrokerizeError } from "./errors";
import { createPollingSubscription } from "./pollingSubscription";
import * as openApiClient from "./swagger";
Expand Down Expand Up @@ -171,13 +176,14 @@ export class AuthorizedApiContext {
throw new Error("AuthorizedApiContext is destroyed");
}
const tok = await this._auth.getToken();
const headers = await withAcceptLanguage(this._cfg, {
"x-brkrz-client-id": this._cfg.clientId,
Authorization: "Bearer " + tok.idToken,
"Content-Type": "application/json",
});
return {
signal: this._abortController.signal,
headers: {
"x-brkrz-client-id": this._cfg.clientId,
Authorization: "Bearer " + tok.idToken,
"Content-Type": "application/json",
},
headers,
};
}
async getBrokers() {
Expand Down
19 changes: 10 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
GuestAuthContextConfiguration,
RegisteredUserAuthContextConfiguration,
TokenSet,
withAcceptLanguage,
} from "./apiCtx";
import {
AuthorizedApiContext,
Expand Down Expand Up @@ -108,10 +109,10 @@ export class Brokerize {
): Promise<GuestAuthContextConfiguration> {
const response = await fetch(this._cfg.basePath + "/user/token", {
method: "POST",
headers: {
headers: await withAcceptLanguage(this._cfg, {
"x-brkrz-client-id": this._cfg.clientId,
"Content-Type": "application/x-www-form-urlencoded",
},
}),
// XXX some runtimes do not have URLSearchParams, so just produce the body in the old-fashioned way
body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(
refreshToken,
Expand Down Expand Up @@ -159,10 +160,10 @@ export class Brokerize {
async createGuestUser(): Promise<AuthContextConfiguration> {
const updatedAt = Date.now();
const user = await this._userApi.createGuestUser({
headers: {
headers: await withAcceptLanguage(this._cfg, {
"x-brkrz-client-id": this._cfg.clientId,
"Content-Type": "application/json",
},
}),
});
return {
type: "guest",
Expand Down Expand Up @@ -218,16 +219,16 @@ export class Brokerize {
});
}

checkRecoveryPhrase(recoveryPhrase: string) {
async checkRecoveryPhrase(recoveryPhrase: string) {
return this._userApi.checkRecoveryPhrase(
{
obtainTokenByRecoveryPhraseParams: { recoveryPhrase },
},
{
headers: {
headers: await withAcceptLanguage(this._cfg, {
"x-brkrz-client-id": this._cfg.clientId,
"Content-Type": "application/json",
},
}),
},
);
}
Expand All @@ -240,10 +241,10 @@ export class Brokerize {
obtainTokenByRecoveryPhraseParams: { recoveryPhrase },
},
{
headers: {
headers: await withAcceptLanguage(this._cfg, {
"x-brkrz-client-id": this._cfg.clientId,
"Content-Type": "application/json",
},
}),
},
);

Expand Down
Loading