> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rocketpunch.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Implement the Rocketpunch OAuth 2.0 Authorization Flow

> Step-by-step guide to implementing the OAuth 2.0 authorization code flow for the Rocketpunch API, from initial redirect to token exchange.

Implementing the Rocketpunch OAuth 2.0 flow takes four steps: redirect the user to Rocketpunch, receive the authorization code at your callback URL, exchange that code for an access token, and use the token to make user-scoped API calls. This guide walks through each step with working code examples.

Before you start, make sure you have completed the [OAuth setup](/api/oauth/overview#setting-up-oauth) — you will need your **Client ID**, **Client Secret**, and a registered **redirect URI**.

<Warning>
  Never expose your Client Secret in client-side JavaScript, mobile apps, or public source code. All token exchange requests must originate from your server.
</Warning>

## Step 1: Redirect the User to the Authorization URL

When a user wants to connect their Rocketpunch account to your app, redirect them to Rocketpunch's authorization endpoint. Build the URL with the required query parameters and send the user there.

**Authorization endpoint:**

```
https://www.rocketpunch.com/oauth/authorize
```

**Required parameters:**

| Parameter       | Value                      | Description                                        |
| --------------- | -------------------------- | -------------------------------------------------- |
| `client_id`     | Your app's Client ID       | Identifies your application                        |
| `redirect_uri`  | Your callback URL          | Must match the URI registered in your app settings |
| `response_type` | `code`                     | Requests an authorization code                     |
| `scope`         | Space-separated scope list | Permissions your app is requesting                 |

**Available scopes:**

| Scope           | Description                                |
| --------------- | ------------------------------------------ |
| `profile:read`  | Read the user's public profile information |
| `profile:email` | Access the user's email address            |

<CodeGroup>
  ```javascript Node.js theme={null}
  const params = new URLSearchParams({
    client_id: process.env.RP_CLIENT_ID,
    redirect_uri: "https://yourapp.com/auth/rocketpunch/callback",
    response_type: "code",
    scope: "profile:read profile:email",
  });

  const authorizationUrl = `https://www.rocketpunch.com/oauth/authorize?${params.toString()}`;

  // In an Express route handler:
  res.redirect(authorizationUrl);
  ```

  ```python Python theme={null}
  import os
  from urllib.parse import urlencode

  params = urlencode({
      "client_id": os.environ["RP_CLIENT_ID"],
      "redirect_uri": "https://yourapp.com/auth/rocketpunch/callback",
      "response_type": "code",
      "scope": "profile:read profile:email",
  })

  authorization_url = f"https://www.rocketpunch.com/oauth/authorize?{params}"

  # In a Flask route handler:
  return redirect(authorization_url)
  ```

  ```bash cURL (for testing) theme={null}
  curl -G "https://www.rocketpunch.com/oauth/authorize" \
    --data-urlencode "client_id=YOUR_CLIENT_ID" \
    --data-urlencode "redirect_uri=https://yourapp.com/auth/rocketpunch/callback" \
    --data-urlencode "response_type=code" \
    --data-urlencode "scope=profile:read profile:email"
  ```
</CodeGroup>

## Step 2: User Grants Consent

After you redirect the user, Rocketpunch takes over. The user sees a login screen (if not already signed in) followed by a consent screen showing your app's name and the permissions you requested.

* If the user **approves**, Rocketpunch redirects them to your `redirect_uri` with an `?code=` query parameter.
* If the user **denies** access or cancels, Rocketpunch redirects to your `redirect_uri` with an `?error=access_denied` parameter.

Your callback handler must check for both outcomes.

## Step 3: Receive the Authorization Code

Rocketpunch redirects the user to your `redirect_uri` after the consent decision. Extract the `code` parameter from the query string and pass it to your token exchange logic.

```
https://yourapp.com/auth/rocketpunch/callback?code=AUTH_CODE_HERE
```

<CodeGroup>
  ```javascript Node.js (Express) theme={null}
  app.get("/auth/rocketpunch/callback", async (req, res) => {
    const { code, error } = req.query;

    if (error) {
      // User denied access or an error occurred
      return res.redirect("/connect-failed");
    }

    if (!code) {
      return res.status(400).send("Missing authorization code");
    }

    // Proceed to token exchange (Step 4)
    const token = await exchangeCodeForToken(code);
    // Store token and redirect the user
    req.session.accessToken = token.access_token;
    res.redirect("/dashboard");
  });
  ```

  ```python Python (Flask) theme={null}
  from flask import request, redirect, session

  @app.route("/auth/rocketpunch/callback")
  def rocketpunch_callback():
      error = request.args.get("error")
      code = request.args.get("code")

      if error:
          return redirect("/connect-failed")

      if not code:
          return "Missing authorization code", 400

      # Proceed to token exchange (Step 4)
      token = exchange_code_for_token(code)
      session["access_token"] = token["access_token"]
      return redirect("/dashboard")
  ```
</CodeGroup>

## Step 4: Exchange the Code for an Access Token

The authorization code is short-lived and single-use. Your server must exchange it for an access token by making a POST request to Rocketpunch's token endpoint.

**Token endpoint:**

```
POST https://www.rocketpunch.com/oauth/token
```

**Required parameters:**

| Parameter       | Value                                |
| --------------- | ------------------------------------ |
| `grant_type`    | `authorization_code`                 |
| `code`          | The authorization code from Step 3   |
| `client_id`     | Your app's Client ID                 |
| `client_secret` | Your app's Client Secret             |
| `redirect_uri`  | The same redirect URI used in Step 1 |

<CodeGroup>
  ```javascript Node.js theme={null}
  async function exchangeCodeForToken(code) {
    const response = await fetch("https://www.rocketpunch.com/oauth/token", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        grant_type: "authorization_code",
        code: code,
        client_id: process.env.RP_CLIENT_ID,
        client_secret: process.env.RP_CLIENT_SECRET,
        redirect_uri: "https://yourapp.com/auth/rocketpunch/callback",
      }),
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`Token exchange failed: ${error.error}`);
    }

    return response.json();
    // Returns: { access_token, token_type, expires_in, refresh_token, scope }
  }
  ```

  ```python Python theme={null}
  import os
  import requests

  def exchange_code_for_token(code):
      response = requests.post(
          "https://www.rocketpunch.com/oauth/token",
          data={
              "grant_type": "authorization_code",
              "code": code,
              "client_id": os.environ["RP_CLIENT_ID"],
              "client_secret": os.environ["RP_CLIENT_SECRET"],
              "redirect_uri": "https://yourapp.com/auth/rocketpunch/callback",
          },
          headers={"Content-Type": "application/x-www-form-urlencoded"},
      )
      response.raise_for_status()
      return response.json()
      # Returns: { access_token, token_type, expires_in, refresh_token, scope }
  ```

  ```bash cURL theme={null}
  curl -X POST "https://www.rocketpunch.com/oauth/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=authorization_code" \
    -d "code=AUTH_CODE_HERE" \
    -d "client_id=YOUR_CLIENT_ID" \
    -d "client_secret=YOUR_CLIENT_SECRET" \
    -d "redirect_uri=https://yourapp.com/auth/rocketpunch/callback"
  ```
</CodeGroup>

A successful response returns a JSON object with the access token:

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200abc...",
  "scope": "profile:read profile:email"
}
```

Store the `access_token` and `refresh_token` securely in your backend — never in a browser's `localStorage` or a mobile app's unsecured storage.

## Step 5: Use the Access Token

Include the access token in the `Authorization` header of every user-specific API request. Use the `Bearer` scheme.

<CodeGroup>
  ```javascript Node.js theme={null}
  async function getUserProfile(accessToken) {
    const response = await fetch(
      "https://openapi.rocketpunch.com/api/v1/profiles/me",
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
          "Content-Type": "application/json",
        },
      }
    );

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`API request failed: ${error.error.message}`);
    }

    return response.json();
  }
  ```

  ```python Python theme={null}
  import requests

  def get_user_profile(access_token):
      response = requests.get(
          "https://openapi.rocketpunch.com/api/v1/profiles/me",
          headers={
              "Authorization": f"Bearer {access_token}",
              "Content-Type": "application/json",
          },
      )
      response.raise_for_status()
      return response.json()
  ```

  ```bash cURL theme={null}
  curl "https://openapi.rocketpunch.com/api/v1/profiles/me" \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json"
  ```
</CodeGroup>

## Token Refresh

Access tokens expire after the period indicated in `expires_in` (in seconds). When a token expires, use the `refresh_token` to obtain a new access token without requiring the user to go through the consent flow again.

<CodeGroup>
  ```javascript Node.js theme={null}
  async function refreshAccessToken(refreshToken) {
    const response = await fetch("https://www.rocketpunch.com/oauth/token", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        grant_type: "refresh_token",
        refresh_token: refreshToken,
        client_id: process.env.RP_CLIENT_ID,
        client_secret: process.env.RP_CLIENT_SECRET,
      }),
    });

    if (!response.ok) {
      // Refresh token may have expired — prompt the user to re-authorize
      throw new Error("Token refresh failed. Re-authorization required.");
    }

    return response.json();
  }
  ```

  ```python Python theme={null}
  def refresh_access_token(refresh_token):
      response = requests.post(
          "https://www.rocketpunch.com/oauth/token",
          data={
              "grant_type": "refresh_token",
              "refresh_token": refresh_token,
              "client_id": os.environ["RP_CLIENT_ID"],
              "client_secret": os.environ["RP_CLIENT_SECRET"],
          },
          headers={"Content-Type": "application/x-www-form-urlencoded"},
      )
      if not response.ok:
          # Refresh token may have expired — prompt the user to re-authorize
          raise Exception("Token refresh failed. Re-authorization required.")
      return response.json()
  ```
</CodeGroup>

<Tip>
  Proactively refresh the access token before it expires rather than waiting for a `401 Unauthorized` response. Track the `expires_in` value and schedule a refresh a few minutes early to keep your users' sessions seamless.
</Tip>

If the refresh token itself has expired or been revoked, you must redirect the user through the full authorization flow again.
