diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts index 80ea89563b..fae4f35cae 100644 --- a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts +++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts @@ -162,7 +162,7 @@ export class OAuthClientUsersController { @HttpCode(HttpStatus.OK) @ApiOperation({ summary: "Force refresh tokens", - description: `If you have lost managed user access or refresh token, then you can get new ones by using OAuth credentials. ${TOKENS_DOCS}`, + description: `If you have lost managed user access or refresh token or the refresh token has expired, then you can get new ones by using OAuth credentials. ${TOKENS_DOCS}`, }) @MembershipRoles([MembershipRole.ADMIN, MembershipRole.OWNER]) async forceRefresh( diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts index 80db24b982..5cf932d52d 100644 --- a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts +++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts @@ -33,7 +33,8 @@ import { Response as ExpressResponse } from "express"; import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants"; export const TOKENS_DOCS = `Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model \`calAccessToken\` and \`calRefreshToken\` fields. -Response also contains \`accessTokenExpiresAt\` and \`refreshTokenExpiresAt\` fields, but if you decode the jwt token the payload will contain \`clientId\` (OAuth client ID), \`ownerId\` (user to whom token belongs ID), \`iat\` (issued at time) and \`expiresAt\` (when does the token expire) fields.`; +Response also contains \`accessTokenExpiresAt\` and \`refreshTokenExpiresAt\` fields, but if you decode the jwt token the payload will contain \`clientId\` (OAuth client ID), \`ownerId\` (user to whom token belongs ID), \`iat\` (issued at time) and \`expiresAt\` (when does the token expire) fields. +To learn about managing refresh tokens expiring after 1 year follow [this guide](https://cal.com/docs/platform/quickstart#5-managing-refresh-token-expiry)`; @Controller({ path: "/v2/oauth/:clientId", diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index 9da64e7652..5bb2023981 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -276,7 +276,7 @@ "post": { "operationId": "OAuthClientUsersController_forceRefresh", "summary": "Force refresh tokens", - "description": "If you have lost managed user access or refresh token, then you can get new ones by using OAuth credentials. Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model `calAccessToken` and `calRefreshToken` fields.\nResponse also contains `accessTokenExpiresAt` and `refreshTokenExpiresAt` fields, but if you decode the jwt token the payload will contain `clientId` (OAuth client ID), `ownerId` (user to whom token belongs ID), `iat` (issued at time) and `expiresAt` (when does the token expire) fields.", + "description": "If you have lost managed user access or refresh token or the refresh token has expired, then you can get new ones by using OAuth credentials. Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model `calAccessToken` and `calRefreshToken` fields.\nResponse also contains `accessTokenExpiresAt` and `refreshTokenExpiresAt` fields, but if you decode the jwt token the payload will contain `clientId` (OAuth client ID), `ownerId` (user to whom token belongs ID), `iat` (issued at time) and `expiresAt` (when does the token expire) fields.\nTo learn about managing refresh tokens expiring after 1 year follow [this guide](https://cal.com/docs/platform/quickstart#5-managing-refresh-token-expiry)", "parameters": [ { "name": "x-cal-secret-key", @@ -325,7 +325,7 @@ "post": { "operationId": "OAuthFlowController_refreshTokens", "summary": "Refresh managed user tokens", - "description": "If managed user access token is expired then get a new one using this endpoint - it will also refresh the refresh token, because we use\n \"refresh token rotation\" mechanism. Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model `calAccessToken` and `calRefreshToken` fields.\nResponse also contains `accessTokenExpiresAt` and `refreshTokenExpiresAt` fields, but if you decode the jwt token the payload will contain `clientId` (OAuth client ID), `ownerId` (user to whom token belongs ID), `iat` (issued at time) and `expiresAt` (when does the token expire) fields.", + "description": "If managed user access token is expired then get a new one using this endpoint - it will also refresh the refresh token, because we use\n \"refresh token rotation\" mechanism. Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model `calAccessToken` and `calRefreshToken` fields.\nResponse also contains `accessTokenExpiresAt` and `refreshTokenExpiresAt` fields, but if you decode the jwt token the payload will contain `clientId` (OAuth client ID), `ownerId` (user to whom token belongs ID), `iat` (issued at time) and `expiresAt` (when does the token expire) fields.\nTo learn about managing refresh tokens expiring after 1 year follow [this guide](https://cal.com/docs/platform/quickstart#5-managing-refresh-token-expiry)", "parameters": [ { "name": "clientId", diff --git a/docs/api-reference/v2/openapi.json b/docs/api-reference/v2/openapi.json index 634bbfa9f5..9e0e14e09c 100644 --- a/docs/api-reference/v2/openapi.json +++ b/docs/api-reference/v2/openapi.json @@ -266,7 +266,7 @@ "post": { "operationId": "OAuthClientUsersController_forceRefresh", "summary": "Force refresh tokens", - "description": "If you have lost managed user access or refresh token, then you can get new ones by using OAuth credentials. Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model `calAccessToken` and `calRefreshToken` fields.\nResponse also contains `accessTokenExpiresAt` and `refreshTokenExpiresAt` fields, but if you decode the jwt token the payload will contain `clientId` (OAuth client ID), `ownerId` (user to whom token belongs ID), `iat` (issued at time) and `expiresAt` (when does the token expire) fields.", + "description": "If you have lost managed user access or refresh token or the refresh token has expired, then you can get new ones by using OAuth credentials. Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model `calAccessToken` and `calRefreshToken` fields.\nResponse also contains `accessTokenExpiresAt` and `refreshTokenExpiresAt` fields, but if you decode the jwt token the payload will contain `clientId` (OAuth client ID), `ownerId` (user to whom token belongs ID), `iat` (issued at time) and `expiresAt` (when does the token expire) fields.\nTo learn about managing refresh tokens expiring after 1 year follow [this guide](https://cal.com/docs/platform/quickstart#5-managing-refresh-token-expiry)", "parameters": [ { "name": "x-cal-secret-key", @@ -313,7 +313,7 @@ "post": { "operationId": "OAuthFlowController_refreshTokens", "summary": "Refresh managed user tokens", - "description": "If managed user access token is expired then get a new one using this endpoint - it will also refresh the refresh token, because we use\n \"refresh token rotation\" mechanism. Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model `calAccessToken` and `calRefreshToken` fields.\nResponse also contains `accessTokenExpiresAt` and `refreshTokenExpiresAt` fields, but if you decode the jwt token the payload will contain `clientId` (OAuth client ID), `ownerId` (user to whom token belongs ID), `iat` (issued at time) and `expiresAt` (when does the token expire) fields.", + "description": "If managed user access token is expired then get a new one using this endpoint - it will also refresh the refresh token, because we use\n \"refresh token rotation\" mechanism. Access token is valid for 60 minutes and refresh token for 1 year. Make sure to store them in your database, for example, in your User database model `calAccessToken` and `calRefreshToken` fields.\nResponse also contains `accessTokenExpiresAt` and `refreshTokenExpiresAt` fields, but if you decode the jwt token the payload will contain `clientId` (OAuth client ID), `ownerId` (user to whom token belongs ID), `iat` (issued at time) and `expiresAt` (when does the token expire) fields.\nTo learn about managing refresh tokens expiring after 1 year follow [this guide](https://cal.com/docs/platform/quickstart#5-managing-refresh-token-expiry)", "parameters": [ { "name": "clientId", diff --git a/docs/platform/atoms/event-type.mdx b/docs/platform/atoms/event-type.mdx index 83659669b1..04a8017f33 100644 --- a/docs/platform/atoms/event-type.mdx +++ b/docs/platform/atoms/event-type.mdx @@ -45,9 +45,17 @@ For a demonstration of the create event type atom, please refer to the video bel ## Team event type -For creating an event type for a team, you need to provide the team id of your particular team as a prop to the create event type atom. Also note that a team event type can only be created by a team admin or owner. - -Below code snippet can be used to render the create event type atom for a team. +For creating an event type for a team, you need to provide the team id of your particular team as a prop to the create event type atom. Importantly, a team event type can only be created by a managed user who has an accepted admin or owner role within the team. That means +you have to create a managed user and then add an accepted membership with an admin or owner role by making a request to the [memberships endpoint](https://cal.com/docs/api-reference/v2/orgs-teams-memberships/create-a-membership). Example body: +```js +{ + "userId": 1006, + "accepted": true, + "role": "OWNER" +} +``` +then you have to pass the access token of this managed user to the [CalProvider](https://cal.com/docs/platform/atoms/cal-provider) to then finally be able to +use the `` component while passing `teamId={teamId}` to it to create a team event type: ```js import { CreateEventType } from "@calcom/atoms"; @@ -130,6 +138,18 @@ export function EventTypeTabs(eventTypeId: number) { } ``` +If the `eventTypeId` is of a team event type id, then only the owner or admin of the team can update the event type settings. That means +you have to create a managed user and then add an accepted membership with an admin or owner role by making a request to the [memberships endpoint](https://cal.com/docs/api-reference/v2/orgs-teams-memberships/create-a-membership). Example body: +```js +{ + "userId": 1006, + "accepted": true, + "role": "OWNER" +} +``` +then you have to pass the access token of this managed user to the [CalProvider](https://cal.com/docs/platform/atoms/cal-provider) to then finally be able to +use the `` component while passing id of a team event type enabling admin or owner to edit team event types. + For a demonstration of the event type settings atom, please refer to the video below.

diff --git a/docs/platform/quickstart.mdx b/docs/platform/quickstart.mdx index 144086f263..d9ad454f92 100644 --- a/docs/platform/quickstart.mdx +++ b/docs/platform/quickstart.mdx @@ -95,7 +95,7 @@ We recommend setting the `timeZone` for ease of use. You have to setup an endpoint on your server to which atoms can send an expired access token and receive new one in return. This exchange will be automatically handled by atoms when you provide this endpoint URL when setting up frontend in the next step. > Q: Why do we need a separate endpoint just for this? -A: Your OAuth client secret and users refresh tokens should never be exposed on the frontend. The OAuth client secret will reside as an environment variable in your backend and the users' refresh tokens are stored in your database, which are used by the SDK to refresh the access token. +A: Your OAuth client secret and users refresh tokens should never be exposed on the frontend. The OAuth client secret will reside as an environment variable in your backend and the users' refresh tokens are stored in your database, which are used by our API to refresh the access token. > You can check an example refresh token endpoint in our atoms examples app: [https://github.com/calcom/atoms-examples/blob/main/cal-sync/src/pages/api/refresh.ts](https://github.com/calcom/atoms-examples/blob/main/cal-sync/src/pages/api/refresh.ts) @@ -109,20 +109,31 @@ Shortly: 2. Your endpoint will receive a request from atoms. 3. Make it a GET endpoint to which “Authorization: Bearer accessToken” header can be sent to. 4. Assuming you have stored the access and refresh tokens in your database and connected them to a specific user, fetch the user based on the received access token. -5. Provide the OAuth client ID, client secret and refresh token and make a request to our “oauth/:clientId/refresh” endpoint. -6. Store access and refresh tokens returned by the SDK in your database. +5. Provide the OAuth client ID, client secret and managed user refresh token to the [`/refresh`](https://cal.com/docs/api-reference/v2/platform-managed-users/refresh-managed-user-tokens) endpoint. +6. Store in your database access and refresh tokens returned by the [`/refresh`](https://cal.com/docs/api-reference/v2/platform-managed-users/refresh-managed-user-tokens) endpoint. 7. Return access token to the request. It should be in the format of: ```js { accessToken: "fresh access token" } ``` -The atoms will now use the new access token to handle scheduling on behalf of your user. +The atoms will now use the new access token to handle scheduling on behalf of your user. Access token expires after 60 minutes and atoms +then will make a request to your refresh token endpoint to get a new access token while also refreshing the refresh token. -## 5. Frontend: setting up atoms +## 5. Managing refresh token expiry + +Each refresh token is valid for 1 year. When a managed user's access token is refreshed, the refresh token is refreshed too. +However, to make sure that refresh tokens do not expire for less active users you have 2 options: +1. If you decode the refresh token there is `expiresAt` date (it is a jwt token so you can decode it using some library from npm). You could have a cron job that checks all of your users refresh tokens and sees if the refresh token +is about to expire, then refresh tokens using the [`/refresh`](https://cal.com/docs/api-reference/v2/platform-managed-users/refresh-managed-user-tokens) endpoint. +2. In the refresh endpoint you built in the [previous step](https://cal.com/docs/platform/quickstart#4-backend%3A-setting-up-a-refresh-token-endpoint) you could have a check that if the call to [`/refresh`](https://cal.com/docs/api-reference/v2/platform-managed-users/refresh-managed-user-tokens) fails meaning that the +refresh token has expired, call the [`/force-refresh`](https://cal.com/docs/api-reference/v2/platform-managed-users/force-refresh-tokens) endpoint - it allows you to refresh managed user tokens +using only the OAuth client ID and client secret and is intended to be used in cases when one of the tokens is lost or the refresh token is expired. + +## 6. Frontend: setting up atoms Atoms are customizable UI components handling scheduling on behalf of your users. -### 5.1 Install the atoms package +### 6.1 Install the atoms package npm: @@ -142,7 +153,7 @@ pnpm: pnpm add @calcom/atoms ``` -### 5.2 Setup environment variables +### 6.2 Setup environment variables CAL_OAUTH_CLIENT_ID: OAuth client ID @@ -227,7 +238,7 @@ function MyApp({ Component, pageProps }) { export default MyApp; ``` -## 6. Frontend: using atoms +## 7. Frontend: using atoms It’s very easy, just import the atom and drop it in code! For example, for users to connect their Google Calendar drop in "Connect.GoogleCalendar" component - it will handle everything. @@ -274,7 +285,7 @@ other more complex atoms will expose multiple classNames and props to react to e />; ``` -## 7. Front-end: passing additional props to CalProvider +## 8. Front-end: passing additional props to CalProvider The CalProvider component offers several additional props to customize its behavior and appearance: 1. autoUpdateTimezone: By default, the atoms automatically update the user's timezone. You can disable this feature by setting autoUpdateTimezone to false.