DEV Community

Cover image for How to Handle OAuth 2.0 Authentication to Third-Party APIs with Asstgr (self-hosted)
GREVE Malick
GREVE Malick

Posted on

How to Handle OAuth 2.0 Authentication to Third-Party APIs with Asstgr (self-hosted)

If you've ever had to integrate an OAuth 2.0-protected third-party API into multiple projects, you know the drill: handle the authorization flow, store the tokens, track their expiration, build automatic refresh logic... and start over on every new project.

Asstgr is a self-hosted API gateway (Django + DRF) that centralizes all of this. The idea: you register a third-party API once in Asstgr, describe its endpoints, and then call it through a unified REST interface — Asstgr takes care of authentication, quota, and logging on your behalf.

In this article, we'll focus on one specific use case: how to connect an OAuth 2.0-protected API to Asstgr, and call it without ever handling a token by hand.

The concept
Your app ──► Asstgr (/api/v1/...execute/) ──► OAuth2-protected third-party API

├─ Auth (API Key or OAuth2)
├─ Quota
├─ Logs
└─ Response formatting

Your application only needs to know one thing: your Asstgr API key (sk-...). Asstgr internally handles all exchanges with the third-party API's OAuth server (fetching, caching, and refreshing tokens).

Supported flows

Asstgr supports the three most common OAuth 2.0 grants, with automatic token refresh:

client_credentials — for server-to-server integrations (the most common case)
authorization_code — for APIs requiring explicit user authorization
password — for legacy ROPC-based APIs

Step 1 — Register the API

We start like with any other API in Asstgr:

http
POST /api/v1/apis/
{
  "name": "My Protected API",
  "url": "https://api.example.com/v1",
  "auth_required": true,
  "quota_cost": 2
}
Enter fullscreen mode Exit fullscreen mode

Step 2 — Configure OAuth 2.0

This is where it gets interesting. We attach an OAuth configuration to the API through the dedicated endpoint:

http
POST /api/v1/apis/{api_id}/oauth/
{
  "grant_type": "client_credentials",
  "token_url": "https://api.example.com/oauth/token",
  "client_id": "your_client_id",
  "client_secret": "your_client_secret",
  "scope": "read write"
}
Enter fullscreen mode Exit fullscreen mode

The client_secret is stored encrypted server-side (client_secret_encrypted in the database). Once this configuration is saved, Asstgr knows how to obtain a token for this API.

Checking token status

You can check at any time whether a valid token is currently cached:

http
GET /api/v1/apis/{api_id}/oauth/token/
Enter fullscreen mode Exit fullscreen mode

Forcing a refresh

If needed (debugging, secret rotation on the provider's side, etc.), you can manually force a token refresh:

http
POST /api/v1/apis/{api_id}/oauth/token/
Enter fullscreen mode Exit fullscreen mode

Under normal circumstances this isn't necessary: Asstgr's internal service (OAuthService) checks token expiration (token_expires_at) before every call and refreshes it automatically if needed, completely transparently.

Step 3 — Describe the endpoint and its parameters

Just like with a regular API, we add the endpoint and its parameters:

http
POST /api/v1/apis/{api_id}/endpoints/
{
  "path": "/protected-resource",
  "description": "OAuth2-protected resource"
}
http
POST /api/v1/apis/{api_id}/endpoints/{endpoint_id}/parameters/
{
  "name": "resource_id",
  "param_type": "query",
  "data_type": "STRING",
  "required": true
}
http
POST /api/v1/apis/{api_id}/endpoints/{endpoint_id}/methods/
{ "method": "GET" }
Enter fullscreen mode Exit fullscreen mode

Step 4 — Execute the call

And here's the main payoff: from your application, a single call, using your Asstgr key — no OAuth token to manage:

http
POST /api/v1/apis/{api_id}/endpoints/{endpoint_id}/execute/
Authorization: Api-Key sk-xxxxxxxxxxxxxxxxxxxxxxxx

{
  "method": "GET",
  "params": { "resource_id": "42" },
  "display_format": "standard"
}
Enter fullscreen mode Exit fullscreen mode

Asstgr will, in order:

Verify your API key and remaining quota
Fetch (or refresh) the OAuth2 token associated with that third-party API
Build the actual HTTP request with that token in the Authorization header
Call the third-party API
Log the call (APILog: user, endpoint, method, status, response size)
Format and return the response

json
{
  "status_code": 200,
  "result": "...",
  "quota": {
    "used": 4,
    "remaining": 96,
    "limit": 100,
    "usage_pct": 4.0
  }
}
Enter fullscreen mode Exit fullscreen mode

Why centralize this instead of handling OAuth in every service?
A single place to store secrets — your client_id / client_secret don't end up scattered across ten different microservices' codebases
Automatic, shared refresh — the token is cached and refreshed once, even if multiple services call the same API
Unified quota and logging — you know exactly who's calling what, and how much it costs in credits
Transparent provider changes — if the third-party API changes its token URL or scopes, there's only one place to update
Going further

Everything else (API keys, quotas, rate limiting, response formats) follows the same simple pattern: register declaratively once, then call it through /execute/.

The project is open source (Django 5.x + DRF, PostgreSQL):

👉 github.com/asstgr/asstgropensource

If you find the project useful, a ⭐ goes a long way, and you can follow updates on @asstgrio.

Top comments (0)