> For the complete documentation index, see [llms.txt](https://docs.partssource.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.partssource.com/api/authentication/oauth-2.0-flow.md).

# Obtaining Tokens

How to obtain and use access tokens

This guide explains how to integrate the PartsSource API into your application using OAuth 2.0 authorization. For general OAuth 2.0 concepts, refer to the [OAuth 2.0 protocol specification](https://oauth.net/2/).

***

### Prerequisites

Before you begin, you need:

* **Client ID** - Your application identifier
* **Client Secret** - Your application secret (keep this secure!)

Don't have credentials? See [Requesting API Access](/api/authentication/requesting-oauth-2.0-client.md).

***

### Token Request

#### Endpoint

```
POST https://auth.partssource.com/oauth2/token
```

#### Headers

| Header         | Value                               |
| -------------- | ----------------------------------- |
| `Content-Type` | `application/x-www-form-urlencoded` |

#### Parameters

| Parameter       | Required | Description                  |
| --------------- | -------- | ---------------------------- |
| `grant_type`    | Yes      | Must be `client_credentials` |
| `client_id`     | Yes      | Your client ID               |
| `client_secret` | Yes      | Your client secret           |

***

### Request Examples

#### cURL

```bash
curl -X POST https://auth.partssource.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"
```

#### C\#

```csharp
public class TokenService
{
    private readonly HttpClient _httpClient;
    private readonly string _clientId;
    private readonly string _clientSecret;

    private string _accessToken;
    private DateTime _tokenExpiry;

    public TokenService(string clientId, string clientSecret)
    {
        _httpClient = new HttpClient();
        _clientId = clientId;
        _clientSecret = clientSecret;
    }

    public async Task<string> GetAccessTokenAsync()
    {
        // Return cached token if still valid (with 5-minute buffer)
        if (!string.IsNullOrEmpty(_accessToken) && DateTime.UtcNow < _tokenExpiry.AddMinutes(-5))
        {
            return _accessToken;
        }

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            new KeyValuePair<string, string>("client_id", _clientId),
            new KeyValuePair<string, string>("client_secret", _clientSecret)
        });

        var response = await _httpClient.PostAsync(
            "https://auth.partssource.com/oauth2/token",
            content);

        response.EnsureSuccessStatusCode();

        var tokenResponse = await response.Content
            .ReadFromJsonAsync<TokenResponse>();

        _accessToken = tokenResponse.AccessToken;
        _tokenExpiry = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn);

        return _accessToken;
    }
}

public class TokenResponse
{
    [JsonPropertyName("access_token")]
    public string AccessToken { get; set; }

    [JsonPropertyName("token_type")]
    public string TokenType { get; set; }

    [JsonPropertyName("expires_in")]
    public int ExpiresIn { get; set; }
}
```

#### TypeScript

```typescript
interface TokenResponse {
  access_token: string;
  token_type: string;
  expires_in: number;
}

class TokenService {
  private accessToken: string | null = null;
  private tokenExpiry: Date | null = null;

  constructor(
    private clientId: string,
    private clientSecret: string
  ) {}

  async getAccessToken(): Promise<string> {
    // Return cached token if still valid (with 5-minute buffer)
    if (this.accessToken && this.tokenExpiry) {
      const bufferTime = 5 * 60 * 1000; // 5 minutes
      if (new Date().getTime() < this.tokenExpiry.getTime() - bufferTime) {
        return this.accessToken;
      }
    }

    const params = new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: this.clientId,
      client_secret: this.clientSecret,
    });

    const response = await fetch('https://auth.partssource.com/oauth2/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: params,
    });

    if (!response.ok) {
      throw new Error(`Token request failed: ${response.status}`);
    }

    const data: TokenResponse = await response.json();

    this.accessToken = data.access_token;
    this.tokenExpiry = new Date(Date.now() + data.expires_in * 1000);

    return this.accessToken;
  }
}

// Usage
const tokenService = new TokenService(
  process.env.CLIENT_ID!,
  process.env.CLIENT_SECRET!
);

const token = await tokenService.getAccessToken();
```

#### Python

```python
import requests
from datetime import datetime, timedelta

class TokenService:
    def __init__(self, client_id: str, client_secret: str):
        self.client_id = client_id
        self.client_secret = client_secret
        self._access_token = None
        self._token_expiry = None

    def get_access_token(self) -> str:
        # Return cached token if still valid (with 5-minute buffer)
        if self._access_token and self._token_expiry:
            if datetime.utcnow() < self._token_expiry - timedelta(minutes=5):
                return self._access_token

        response = requests.post(
            'https://auth.partssource.com/oauth2/token',
            data={
                'grant_type': 'client_credentials',
                'client_id': self.client_id,
                'client_secret': self.client_secret,
            },
            headers={
                'Content-Type': 'application/x-www-form-urlencoded',
            }
        )

        response.raise_for_status()
        data = response.json()

        self._access_token = data['access_token']
        self._token_expiry = datetime.utcnow() + timedelta(seconds=data['expires_in'])

        return self._access_token

# Usage
token_service = TokenService(
    client_id=os.environ['CLIENT_ID'],
    client_secret=os.environ['CLIENT_SECRET']
)

token = token_service.get_access_token()
```

***

### Token Response

#### Success Response

```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMzQ1Njc4OTAifQ.eyJzdWIiOiIxODZ2ZTN2NTA1cnBxNzF2aTI0YnI4YzZlMyIsImNsaWVudF9pZCI6IjE4NnZlM3Y1MDVycHE3MXZpMjRicjhjNmUzIiwic2NvcGUiOiJkZWZhdWx0LW0ybS1yZXNvdXJjZS1zZXJ2ZXItcDJoa2FoL2FkbWluOmludGVybmFsIiwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbS91cy1lYXN0LTFfQUJDREVGRyIsImV4cCI6MTYzNDgyNTQwMCwiaWF0IjoxNjM0ODIxODAwfQ.signature",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

| Field          | Type    | Description                               |
| -------------- | ------- | ----------------------------------------- |
| `access_token` | string  | JWT token to use in API requests          |
| `token_type`   | string  | Always `Bearer`                           |
| `expires_in`   | integer | Token lifetime in seconds (3600 = 1 hour) |

#### Error Responses

| Error                    | HTTP Status | Description                             |
| ------------------------ | ----------- | --------------------------------------- |
| `invalid_request`        | 400         | Missing or invalid parameters           |
| `invalid_client`         | 401         | Invalid client\_id or client\_secret    |
| `unsupported_grant_type` | 400         | Grant type must be `client_credentials` |

**Example error response:**

```json
{
  "error": "invalid_client",
  "error_description": "Client authentication failed"
}
```

***

### Using the Token

Include the access token and API key in all API requests:

```bash
curl -X GET "https://api.partssource.com/customer/api/users/lookup?userId=12345" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "x-api-key: YOUR_API_KEY"
```

#### Required Headers

| Header          | Value                                         |
| --------------- | --------------------------------------------- |
| `Authorization` | `Bearer <access_token>`                       |
| `x-api-key`     | Your API key (provided with your credentials) |

{% hint style="warning" %}
**Note the space** between `Bearer` and the token. A missing space will cause authentication to fail.
{% endhint %}

***

### Token Validation

The API validates your token using these rules:

| Validation     | Requirement                                           |
| -------------- | ----------------------------------------------------- |
| **Issuer**     | Must match the authorization server URL               |
| **Signature**  | Must be signed by the authorization server's RSA keys |
| **Expiration** | Token must not be expired (`exp` claim)               |
| **Audience**   | Validated against configured audience                 |

If validation fails, the API returns `401 Unauthorized`.

***

### JWT Token Structure

The access token is a JWT with three parts:

```
header.payload.signature
```

#### Decoded Payload Example

```json
{
  "sub": "186ve3v505rpq71vi24br8c6e3",
  "client_id": "186ve3v505rpq71vi24br8c6e3",
  "iss": "https://auth.partssource.com/",
  "exp": 1634825400,
  "iat": 1634821800
}
```

| Claim       | Description                         |
| ----------- | ----------------------------------- |
| `sub`       | Subject (your client ID)            |
| `client_id` | Your application's client ID        |
| `iss`       | Token issuer (authorization server) |
| `exp`       | Expiration timestamp (Unix)         |
| `iat`       | Issued at timestamp (Unix)          |

***

### Next Steps

* [**Token Management**](/api/authentication/oauth-2.0-flow/best-practices-and-error-handling.md) - Best practices for token handling
* [**API Reference**](https://github.com/PartsSourceInc/gitbook-documentation/blob/main/documentation/api-reference/overview.md) - Start making API calls


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.partssource.com/api/authentication/oauth-2.0-flow.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
