Skip to content

Separate authToken from secretKey in ThirdwebClient #6983

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
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: 2 additions & 1 deletion apps/dashboard/src/@/constants/thirdweb.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export function getThirdwebClient(

return createThirdwebClient({
teamId: options?.teamId,
secretKey: options?.jwt ? options.jwt : DASHBOARD_THIRDWEB_SECRET_KEY,
secretKey: DASHBOARD_THIRDWEB_SECRET_KEY,
authToken: options?.jwt ?? undefined,
clientId: DASHBOARD_THIRDWEB_CLIENT_ID,
config: {
storage: {
Expand Down
16 changes: 12 additions & 4 deletions packages/thirdweb/src/client/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,23 @@ describe("client", () => {
it("should accept a jwt being passed", () => {
const client = createThirdwebClient({
clientId: "foo",
secretKey: "bar.baz.qux",
authToken: "bar.baz.qux",
});
expect(client.clientId).toBe("foo");
expect(client.secretKey).toBe("bar.baz.qux");
expect(client.authToken).toBe("bar.baz.qux");
expect(client.secretKey).toBeUndefined();
});
it("should throw if clientId is missing with JWT input", () => {

it("should throw an error if authToken is passed as secretKey", () => {
expect(() =>
createThirdwebClient({ secretKey: "bar.baz.qux" }),
).toThrowError(/clientId must be provided when using a JWT secretKey/);
).toThrowError(/have to pass authToken directly/);
});

it("should throw if clientId is missing with JWT input", () => {
expect(() =>
createThirdwebClient({ authToken: "bar.baz.qux", secretKey: "foo" }),
).toThrowError(/have to pass clientId when passing authToken/);
});
});
});
20 changes: 15 additions & 5 deletions packages/thirdweb/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ export type CreateThirdwebClientOptions = Prettify<
| {
clientId: string;
secretKey?: string;
authToken?: string;
}
| {
clientId?: string;
secretKey: string;
authToken?: string;
}
) &
ClientOptions
Expand All @@ -74,6 +76,11 @@ export type CreateThirdwebClientOptions = Prettify<
export type ThirdwebClient = {
readonly clientId: string;
readonly secretKey: string | undefined;
/**
* The auth token for thirdweb dashboard usage.
* @hidden
*/
readonly authToken: string | undefined;
} & Readonly<ClientOptions>;

/**
Expand Down Expand Up @@ -109,16 +116,18 @@ export type ThirdwebClient = {
export function createThirdwebClient(
options: CreateThirdwebClientOptions,
): ThirdwebClient {
const { clientId, secretKey, ...rest } = options;
const { clientId, secretKey, authToken, ...rest } = options;

let realClientId: string | undefined = clientId;

if (authToken && !clientId) {
// always HAVE to also pass clientId when passing auth token
throw new Error("have to pass clientId when passing authToken");
}

if (secretKey) {
if (isJWT(secretKey)) {
// when passing a JWT as secret key we HAVE to also have a clientId
if (!clientId) {
throw new Error("clientId must be provided when using a JWT secretKey");
}
throw new Error("have to pass authToken directly");
} else {
// always PREFER the clientId if provided, only compute it from the secretKey if we don't have a clientId passed explicitly
realClientId = clientId ?? computeClientIdFromSecretKey(secretKey);
Expand All @@ -132,6 +141,7 @@ export function createThirdwebClient(

return {
...rest,
authToken,
clientId: realClientId,
secretKey,
} as const;
Expand Down
31 changes: 10 additions & 21 deletions packages/thirdweb/src/utils/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
detectPlatform,
} from "./detect-platform.js";
import { getServiceKey } from "./domains.js";
import { isJWT } from "./jwt/is-jwt.js";
import { IS_DEV } from "./process.js";

const DEFAULT_REQUEST_TIMEOUT = 60000;
Expand Down Expand Up @@ -45,39 +44,29 @@ export function getClientFetch(client: ThirdwebClient, ecosystem?: Ecosystem) {
if (!headers) {
headers = new Headers();
}
// auth token if secret key === jwt
const authToken =
useAuthToken && client.secretKey && isJWT(client.secretKey)
? client.secretKey
: undefined;
// secret key if secret key !== jwt
const secretKey =
client.secretKey && !isJWT(client.secretKey)
? client.secretKey
: undefined;
const clientId = client.clientId;

// if we have an auth token set, use that (thirdweb dashboard sets this for the user)

// if we have an auth token set & useAuthToken is true, use the auth token (thirdweb dashboard sets this for the user)
// pay urls should never send the auth token, because we always want the "developer" to be the one making the request, not the "end user"
if (
authToken &&
useAuthToken &&
client.authToken &&
!isPayUrl(urlString) &&
!isInAppWalletUrl(urlString) &&
!isBundlerUrl(urlString)
) {
headers.set("authorization", `Bearer ${authToken}`);
headers.set("authorization", `Bearer ${client.authToken}`);
Comment on lines 44 to +57
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code should be modified to maintain backward compatibility with tests that expect JWT tokens to be passed as secretKey. We should check both client.authToken and if client.secretKey is a JWT. For example: if (useAuthToken && (client.authToken || (client.secretKey && isJWT(client.secretKey))) && ...)

Spotted by Diamond (based on CI logs)

Is this helpful? React 👍 or 👎 to let us know.

// if we have a specific teamId set, add it to the request headers
if (client.teamId) {
headers.set("x-team-id", client.teamId);
}
}

if (secretKey) {
headers.set("x-secret-key", secretKey);
// never set BOTH auth header and secret key header at the same time, auth header takes precedence
else if (client.secretKey) {
headers.set("x-secret-key", client.secretKey);
}

if (clientId) {
headers.set("x-client-id", clientId);
if (client.clientId) {
headers.set("x-client-id", client.clientId);
}

if (ecosystem) {
Expand Down
Loading