Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 469
feat(clerk-js,clerk-react,nextjs): Introduce <APIKeys /> AIO component#5858
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
cbee2b1c5c254da1dfa60cb1961e44cfd153a6d206e4d26a56a914c5beb0a447294f7eb2ff5a483789d0de59b533b4c715b15610ea0feb7aa8a4f2fae651494b25f0f69925f78de82197c7e3e0f19825cfb78ddc0b2712cece97b3bdecb551e338ec277b98c5faa5cf4377046137860c5523b1a944ec76af99cabb5c92e60ee39fc57bf20400d6d0bf49a9188ff06fe0f05f257266d9667088736143e08bc0670a2bdc6ff2a0f7150692f7792c4c1d75bc440171a72bf520d8714fa49b44a3fe7c45b106d0b4f050b6de03ee41a272b8096b3375760cd50d3ed7059770130c636d25929163ff712e389873cbf876398cbfb26c11b8518dbbb4aea430aa0adf6c23d4b08d09a9f68579613380f9abdef2feaa6975387926e428894ebaac35e73f7b3b2d54ea6cf0efd2124cb76d7a52029062b2fea996bbc666c0cbbb9613158b72f2b1808282f87215e1e5b2249643a47874acc44b06cacdeb9cb3412b97cee9cfa9075be8f568296796ba7a09de7f8746364790c3203328a9ec2d686b62bfa3056e73c4d46ddfe97438cfbf2b0e1d37c91b9195b7705e2c11e3952dd68eda216906c5874d7db36bb83927d3380b18d2c81c48204ab40e8ad51244f0a3eae7126ca472dc0751649d8462f10da0b5a0edca2a1b2821ab7f878b1e175cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| '@clerk/localizations': patch | ||
| '@clerk/types': patch | ||
| --- | ||
| Add TypeScript types and en-US localization for upcoming `<APIKeys />` component. This component will initially be in early access. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| '@clerk/clerk-js': minor | ||
| '@clerk/nextjs': minor | ||
| '@clerk/clerk-react': minor | ||
| --- | ||
| Add `<APIKeys />` component. This component will initially be in early access and not recommended for production usage just yet. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| import type { | ||
| ApiKeyJSON, | ||
| APIKeyResource, | ||
| APIKeysNamespace, | ||
| CreateAPIKeyParams, | ||
| GetAPIKeysParams, | ||
| RevokeAPIKeyParams, | ||
| } from '@clerk/types'; | ||
| import type { FapiRequestInit } from '@/core/fapiClient'; | ||
| import { APIKey, BaseResource, ClerkRuntimeError } from '../../resources/internal'; | ||
| export class APIKeys implements APIKeysNamespace { | ||
| /** | ||
| * Returns the base options for the FAPI proxy requests. | ||
| */ | ||
| private async getBaseFapiProxyOptions(): Promise<FapiRequestInit> { | ||
| const token = await BaseResource.clerk.session?.getToken(); | ||
| if (!token) { | ||
| throw new ClerkRuntimeError('No valid session token available', { code: 'no_session_token' }); | ||
| } | ||
| return { | ||
| // Set to an empty string because FAPI Proxy does not include the version in the path. | ||
| pathPrefix: '', | ||
| // Set the session token as a Bearer token in the Authorization header for authentication. | ||
| headers: { | ||
| Authorization: `Bearer ${token}`, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| // Set to `same-origin` to ensure cookies and credentials are sent with requests, avoiding CORS issues. | ||
| credentials: 'same-origin', | ||
| }; | ||
| } | ||
| async getAll(params?: GetAPIKeysParams): Promise<APIKeyResource[]> { | ||
| return BaseResource.clerk | ||
| .getFapiClient() | ||
| .request<{ api_keys: ApiKeyJSON[] }>({ | ||
| ...(await this.getBaseFapiProxyOptions()), | ||
| method: 'GET', | ||
| path: '/api_keys', | ||
| search: { | ||
| subject: params?.subject ?? BaseResource.clerk.organization?.id ?? BaseResource.clerk.user?.id ?? '', | ||
| }, | ||
| }) | ||
| .then(res => { | ||
| const apiKeysJSON = res.payload as unknown as { api_keys: ApiKeyJSON[] }; | ||
| return apiKeysJSON.api_keys.map(json => new APIKey(json)); | ||
| }); | ||
| } | ||
| async getSecret(id: string): Promise<string> { | ||
| return BaseResource.clerk | ||
| .getFapiClient() | ||
| .request<{ secret: string }>({ | ||
| ...(await this.getBaseFapiProxyOptions()), | ||
| method: 'GET', | ||
| path: `/api_keys/${id}/secret`, | ||
| }) | ||
| .then(res => { | ||
| const { secret } = res.payload as unknown as { secret: string }; | ||
| return secret; | ||
| }); | ||
| } | ||
| async create(params: CreateAPIKeyParams): Promise<APIKeyResource> { | ||
| const json = ( | ||
| await BaseResource._fetch<ApiKeyJSON>({ | ||
| ...(await this.getBaseFapiProxyOptions()), | ||
| path: '/api_keys', | ||
| method: 'POST', | ||
| body: JSON.stringify({ | ||
| type: params.type ?? 'api_key', | ||
| name: params.name, | ||
| subject: params.subject ?? BaseResource.clerk.organization?.id ?? BaseResource.clerk.user?.id ?? '', | ||
| description: params.description, | ||
| seconds_until_expiration: params.secondsUntilExpiration, | ||
| }), | ||
| }) | ||
| )?.response as ApiKeyJSON; | ||
| return new APIKey(json); | ||
| } | ||
wobsoriano marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| async revoke(params: RevokeAPIKeyParams): Promise<APIKeyResource> { | ||
| const json = ( | ||
| await BaseResource._fetch<ApiKeyJSON>({ | ||
| ...(await this.getBaseFapiProxyOptions()), | ||
| method: 'POST', | ||
| path: `/api_keys/${params.apiKeyID}/revoke`, | ||
| body: JSON.stringify({ | ||
| revocation_reason: params.revocationReason, | ||
| }), | ||
| }) | ||
| )?.response as ApiKeyJSON; | ||
| return new APIKey(json); | ||
| } | ||
wobsoriano marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. wobsoriano marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import type { ApiKeyJSON, APIKeyResource } from '@clerk/types'; | ||
| import { unixEpochToDate } from '../../utils/date'; | ||
| import { BaseResource } from './internal'; | ||
| export class APIKey extends BaseResource implements APIKeyResource { | ||
| pathRoot = '/api_keys'; | ||
| id!: string; | ||
| type!: string; | ||
| name!: string; | ||
| subject!: string; | ||
| scopes!: string[]; | ||
| claims!: Record<string, any> | null; | ||
| revoked!: boolean; | ||
| revocationReason!: string | null; | ||
| expired!: boolean; | ||
| expiration!: Date | null; | ||
| createdBy!: string | null; | ||
| description!: string | null; | ||
| lastUsedAt!: Date | null; | ||
| createdAt!: Date; | ||
| updatedAt!: Date; | ||
| constructor(data: ApiKeyJSON) { | ||
| super(); | ||
| this.fromJSON(data); | ||
| } | ||
| protected fromJSON(data: ApiKeyJSON | null): this { | ||
| if (!data) { | ||
| return this; | ||
| } | ||
| this.id = data.id; | ||
| this.type = data.type; | ||
| this.name = data.name; | ||
| this.subject = data.subject; | ||
| this.scopes = data.scopes; | ||
| this.claims = data.claims; | ||
| this.revoked = data.revoked; | ||
| this.revocationReason = data.revocation_reason; | ||
| this.expired = data.expired; | ||
| this.expiration = data.expiration ? unixEpochToDate(data.expiration) : null; | ||
| this.createdBy = data.created_by; | ||
| this.description = data.description; | ||
| this.lastUsedAt = data.last_used_at ? unixEpochToDate(data.last_used_at) : null; | ||
| this.updatedAt = unixEpochToDate(data.updated_at); | ||
wobsoriano marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| this.createdAt = unixEpochToDate(data.created_at); | ||
| return this; | ||
| } | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❓ This seems to be missing the MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do it, added! | ||
| public __internal_toSnapshot(): ApiKeyJSON { | ||
| return { | ||
| object: 'api_key', | ||
| id: this.id, | ||
| type: this.type, | ||
| name: this.name, | ||
| subject: this.subject, | ||
| scopes: this.scopes, | ||
| claims: this.claims, | ||
| revoked: this.revoked, | ||
| revocation_reason: this.revocationReason, | ||
| expired: this.expired, | ||
| expiration: this.expiration ? this.expiration.getTime() : null, | ||
| created_by: this.createdBy, | ||
| description: this.description, | ||
| last_used_at: this.lastUsedAt ? this.lastUsedAt.getTime() : null, | ||
| created_at: this.createdAt.getTime(), | ||
| updated_at: this.updatedAt.getTime(), | ||
wobsoriano marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| }; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a telemetry record here?
Example:
javascript/packages/clerk-js/src/core/clerk.ts
Line 758 in 48be55b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I missed this, thanks! added