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

# OAuth2 Apps

Create OAuth2 Apps to allow third-party applications to access Veza on behalf of users

OAuth2 Apps enable third-party applications to access Veza resources on behalf of an authenticated user. When a user initiates access from your application, Veza presents a consent screen. After the user grants consent, Veza issues an access token your application can use to call Veza APIs.

OAuth2 Apps use the authorization code flow with PKCE (Proof Key for Code Exchange). PKCE requires a code challenge using the S256 method. Requests without a valid code challenge are rejected.

Use OAuth2 Apps when:

* Building a web application or service that integrates with Veza
* You need delegated user access (the token acts on behalf of the user, not a service account)
* Your application can handle redirect-based flows

OAuth2 Apps are managed from **Administration** > *API Keys* > *OAuth2 Apps*.

## Prerequisites

* Admin role in Veza.
* A redirect URI that uses `http` or `https` and belongs to the base URL domain or its subdomains.

## Create an OAuth2 App

1. Open **Administration** > *API Keys* > *OAuth2 Apps*.
2. Click **Add New OAuth2 App**.
3. Enter a **Client Name** to identify the application.
4. Enter the **Base URL** of your application (for example, `https://yourcompany.com`). Redirect URIs must use the same scheme and belong to this domain or its subdomains.
5. Under **Redirect URI Validation**, optionally select **Allow any redirect URI without validation**. This is not recommended for production use. When enabled, any URI can be used as a redirect target.
6. In the **Redirect URIs** field, enter one or more allowed redirect URIs as a comma-separated list (for example, `https://yourcompany.com/callback, https://yourcompany.com/auth`). These values are enforced when the validation checkbox is not selected.
7. Select the **Allowed scopes** that the app is permitted to request.
8. Click **Create**.

After creating the app, Veza displays the **Client ID** and an initial **Client Secret**. Copy both values. The secret will not be shown again.

## Authorization flow

OAuth2 Apps use the authorization code flow with PKCE. The S256 code challenge method is required.

### Step 1: Authorization request

Redirect the user to:

```http
GET https://<tenant>/oauth/authorize
  ?client_id=<client-id>
  &response_type=code
  &redirect_uri=<redirect-uri>
  &scope=<scope>
  &state=<state>
  &code_challenge=<S256-hashed-verifier>
  &code_challenge_method=S256
```

The `state` parameter is a CSRF protection value that your application generates and verifies when Veza redirects back to your redirect URI. Use a unique, unpredictable value for each authorization request.

The `scope` parameter accepts only scopes that Veza registers and the app is configured to allow. A request that includes an unregistered scope fails, including the `openid` scope that many OpenID Connect libraries add by default. See [OpenID Connect scopes](/4yItIzMvkpAvMVFAamTf/developers/api/authentication/oauth-scopes.md#openid-connect-scopes).

### Consent screen

Veza presents the user with a consent screen that shows the application name and a description of each requested scope. The user can:

* **Allow**: Grant the application access. Veza redirects to your `redirect_uri` with an authorization `code` and the `state` value you provided.
* **Cancel**: Deny access. Veza redirects to your `redirect_uri` with the standard OAuth2 error parameters `error=access_denied`.

### Step 2: Token exchange

Exchange the authorization code for an access token:

```bash
curl -X POST https://<tenant>/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "client_id=<client-id>" \
  -d "client_secret=<client-secret>" \
  -d "code=<authorization-code>" \
  -d "redirect_uri=<redirect-uri>" \
  -d "code_verifier=<original-verifier>"
```

A successful response:

```json
{
  "access_token": "<token>",
  "refresh_token": "<refresh-token>",
  "token_type": "Bearer",
  "expires_in": 43200,
  "scope": "<scope>"
}
```

Access tokens expire after 12 hours. Authorization codes expire after 10 minutes and can only be used once.

### Step 3: Refresh the token

Refresh tokens expire after 30 days. Each refresh request issues a new access token and a new refresh token, revoking the previous pair. Use the new refresh token for subsequent refresh requests.

```bash
curl -X POST https://<tenant>/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "client_id=<client-id>" \
  -d "client_secret=<client-secret>" \
  -d "refresh_token=<refresh-token>"
```

### Use the access token

Include the token as a Bearer token in the `Authorization` header:

```bash
curl -H "Authorization: Bearer <access-token>" \
  https://<tenant>/api/v1/providers
```

### Get user information

If your application requested the `profile` scope, you can retrieve the authenticated user's profile from the UserInfo endpoint:

```bash
curl -H "Authorization: Bearer <access-token>" \
  https://<tenant>/oauth/userinfo
```

A successful response:

```json
{
  "sub": "<user-id>",
  "email": "user@example.com",
  "given_name": "Jane",
  "family_name": "Doe",
  "name": "Jane Doe"
}
```

This endpoint requires a valid access token with the `profile` scope. Requests without the `profile` scope receive a `401 Unauthorized` response.

## Manage client secrets

Client secrets are used alongside the client ID to authenticate token requests. You can create additional secrets for rotation and delete old ones. Each OAuth2 App supports a maximum of 2 client secrets.

1. From the **OAuth2 Apps** list, locate the app and select **View App Secrets**.
2. Click **Create New Client Secret**.
3. Copy and save the secret value. It will not be shown again. Only the first four characters are displayed for identification after creation.

| Column             | Description                                         |
| ------------------ | --------------------------------------------------- |
| First 4 characters | A partial identifier to distinguish between secrets |
| Created at         | When the secret was generated                       |
| Last used at       | Most recent authentication using this secret        |

Revoked tokens take effect immediately. Veza validates every token against the database on each request, so a deleted or revoked token is rejected on the next use.

To rotate a secret: create a new one, update the application, then wait a few minutes for the new secret to take effect. Verify your application works, then delete the old one.

## Scopes

Scopes control which Veza resources the OAuth2 App can access. Select only the scopes required for your use case. The following scopes support the authorization code grant. See [OAuth Scopes](/4yItIzMvkpAvMVFAamTf/developers/api/authentication/oauth-scopes.md) for the full list of Veza scopes and the grant types each one supports.

| Scope                        | Description                                                                                                                                                                 |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `profile`                    | Read the authenticated user's profile information (name, email). Required for integrations that need to identify the user, such as Slack apps that approve access requests. |
| `lcm:access-requests:manage` | Create and manage Lifecycle Management access requests, including approving, rejecting, assigning approvers, and managing watchers.                                         |

{% hint style="info" %}
**Slack App integration**: The [Slack App Veza Action](/4yItIzMvkpAvMVFAamTf/features/access-reviews/configuration/slack-app.md) uses OAuth2 Apps (authorization code flow with PKCE) to authenticate reviewers when they click Approve or Deny from Slack. Configure the OAuth2 App with the `profile` and `lcm:access-requests:manage` scopes and set the redirect URI to `{veza_url}/slackapp/callback`.
{% endhint %}

## Manage OAuth2 Apps

From the **OAuth2 Apps** list, open the actions menu for an app:

* **Edit App**: Update the app name, base URL, redirect URIs, or allowed scopes.
* **View App Secrets**: View, create, and delete client secrets.
* **Revoke App**: Temporarily disable the app. All active tokens are invalidated immediately.
* **Reinstate App**: Re-enable a revoked app so it can authenticate again. This action replaces **Revoke App** once an app is revoked.
* **Delete App**: Permanently remove the app and all associated secrets and tokens.


---

# 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.veza.com/4yItIzMvkpAvMVFAamTf/developers/api/authentication/oauth2-apps.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.
