# OAuth Authentication Flow in OpenDeepWiki: A Complete Technical Guide

> Discover the OAuth authentication flow in OpenDeepWiki. This technical guide details the Authorization Code flow and JWT token exchange across three architectural layers.

- Repository: [AIDotNet/OpenDeepWiki](https://github.com/aidotnet/opendeepwiki)
- Tags: how-to-guide
- Published: 2026-02-16

---

**OpenDeepWiki implements a standard OAuth 2.0 Authorization Code flow that exchanges provider codes for JWT tokens through three distinct architectural layers.**

The **OAuth authentication flow in OpenDeepWiki** enables secure sign-in via external providers like GitHub and Gitee without storing user passwords. This article examines the complete implementation across endpoint, service, and data layers based on the actual source code in the `AIDotNet/OpenDeepWiki` repository.

## OAuth 2.0 Authorization Code Flow Overview

OpenDeepWiki follows the classic **Authorization Code** grant type defined in RFC 6749. The flow separates user authentication from application authorization by introducing a temporary authorization code that the backend exchanges for an access token. This architecture keeps sensitive tokens server-side while returning a signed **JWT (JSON Web Token)** to the client for subsequent authenticated requests.

## Endpoint Layer: HTTP Routes for OAuth

The public API surface is defined in [[`OAuthEndpoints.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/OAuthEndpoints.cs)](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Endpoints/OAuthEndpoints.cs), which maps minimal API routes to service methods.

### Authorization Endpoint

The `GET /api/oauth/{provider}/authorize` route constructs the provider-specific consent URL. It loads the `OAuthProvider` configuration from the database and returns a JSON payload containing the `authorizationUrl` that the frontend uses to redirect the browser.

### Callback Endpoint

The `GET /api/oauth/{provider}/callback` route receives the **code** and **state** parameters from the provider after user consent. This endpoint delegates to `OAuthService.HandleCallbackAsync`, which orchestrates token exchange, user lookup, and JWT generation. The response returns a `LoginResponse` containing the `AccessToken`, expiration, and user profile.

### Convenience Shortcuts

For direct browser redirects, the endpoint layer exposes shortcut routes such as `/api/oauth/github/login` and `/api/oauth/gitee/login`. These immediately redirect to the provider's consent page without requiring a two-step API call from the client.

## Service Layer: OAuthService Implementation

The core logic resides in [[`OAuthService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/OAuthService.cs)](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Services/OAuth/OAuthService.cs), implementing the `IOAuthService` contract.

### Building the Authorization URL

The `GetAuthorizationUrlAsync` method queries the database for the active `OAuthProvider` record. It constructs the authorization query string with the following required parameters:

- `client_id`
- `redirect_uri`
- `response_type=code`
- `state` (CSRF protection token)
- `scope` (optional, provider-specific permissions)

### Handling the Callback and Token Exchange

The `HandleCallbackAsync` method executes the critical post-redirect sequence:

1. **ExchangeCodeForTokenAsync** – Posts the authorization code to the provider's token endpoint using `client_secret` authentication. Parses the JSON response into an `OAuthTokenResponse` containing `access_token` and optional `refresh_token`.

2. **GetOAuthUserInfoAsync** – Calls the provider's user-info endpoint with the bearer token. Maps the provider-specific JSON structure to a uniform `OAuthUserInfo` object using either custom `UserInfoMapping` configuration or built-in defaults for GitHub and Gitee.

3. **FindOrCreateUserAsync** – Implements the identity linking strategy:
   - Searches for an existing `UserOAuth` binding by provider and external ID.
   - If no binding exists, attempts to match by email address.
   - If no user exists, creates a new `User` record with the default "User" role.
   - Persists a new `UserOAuth` record linking the local user to the external identity.

4. **GetUserRolesAsync** – Retrieves all role names assigned to the user.

5. **IJwtService.GenerateToken** – Creates a signed JWT containing claims for user ID, name, email, avatar URL, and roles.

The final `LoginResponse` includes the `AccessToken`, expiration timestamp, and a `UserInfo` payload ready for client-side storage.

## Data Layer: Provider Configuration and User Bindings

Persistent storage is defined in two key entities:

**`OAuthProvider`** ([[`OAuthProvider.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/OAuthProvider.cs)](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki.Entities/OAuth/OAuthProvider.cs)) stores provider-specific configuration:
- `ClientId` and `ClientSecret`
- `AuthorizationUrl`, `TokenUrl`, and `UserInfoUrl`
- Optional `Scope` string
- Optional `UserInfoMapping` JSON configuration for custom field mapping

**`UserOAuth`** ([[`UserOAuth.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/UserOAuth.cs)](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki.Entities/OAuth/UserOAuth.cs)) records the binding between local users and external identities:
- Foreign keys to `User` and `OAuthProvider`
- External provider user ID
- Access tokens, refresh tokens, and expiry
- Avatar URL and metadata timestamps

## Code Examples

### Server-Side: Initiating the Flow

```csharp
// In a controller or minimal API endpoint
var authUrl = await _oauthService.GetAuthorizationUrlAsync("github");
// Redirect the client to the provider's consent page
return Results.Redirect(authUrl);

```

### Server-Side: Handling the Callback

```csharp
// provider = "github", code = request.Query["code"]
var loginResult = await _oauthService.HandleCallbackAsync(provider, code);
// loginResult.AccessToken contains the JWT for the frontend
return Results.Ok(loginResult);

```

### Client-Side: Next.js Integration

```typescript
// Step 1 – Obtain the authorization URL
const resp = await fetch('/api/oauth/github/authorize');
const { authorizationUrl } = (await resp.json()).data;

// Step 2 – Redirect the browser to the provider
window.location.href = authorizationUrl;

// Step 3 – After the provider redirects back to /api/oauth/github/callback
// The endpoint returns { accessToken, expiresIn, user } which you store
// in memory or secure HTTP-only cookies

```

## Summary

- OpenDeepWiki implements a **standard OAuth 2.0 Authorization Code flow** with clear separation between HTTP endpoints, business logic, and data persistence.
- The **`OAuthService`** class orchestrates the complete lifecycle: URL generation, token exchange, user mapping, and JWT issuance.
- **Provider configuration** is dynamic and database-driven via the `OAuthProvider` entity, supporting custom endpoints and field mappings without code changes.
- **User identity linking** follows a fallback strategy: external ID binding first, then email matching, then new user creation with default roles.

## Frequently Asked Questions

### How does OpenDeepWiki handle the OAuth state parameter for CSRF protection?

The `GetAuthorizationUrlAsync` method in [`OAuthService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/OAuthService.cs) generates a unique `state` value that is included in the authorization request to the provider. When the provider redirects back to the `/api/oauth/{provider}/callback` endpoint, this state parameter is validated to ensure the request originated from the same client session, preventing cross-site request forgery attacks.

### Can OpenDeepWiki support OAuth providers other than GitHub and Gitee?

Yes. The architecture is provider-agnostic. Administrators can add new providers by inserting a record into the `OAuthProvider` table with the appropriate `AuthorizationUrl`, `TokenUrl`, `UserInfoUrl`, and optional `UserInfoMapping` JSON configuration. The `OAuthService` dynamically loads these settings, so no code changes are required to support additional OAuth 2.0 compliant providers.

### What happens if a user already has a local account when they sign in via OAuth?

The `FindOrCreateUserAsync` method implements a cascading identity resolution strategy. First, it checks for an existing `UserOAuth` binding by provider and external ID. If none exists, it attempts to match the user by email address from the OAuth provider. If a matching local user is found, it creates the `UserOAuth` binding to link the accounts. Only if no match is found does it create a new `User` record with the default "User" role.

### How is the JWT token structured after a successful OAuth login?

The JWT is generated by `IJwtService.GenerateToken` and includes claims for the user's ID, display name, email address, avatar URL, and role names collected by `GetUserRolesAsync`. The `LoginResponse` returned to the client contains this `AccessToken`, an `ExpiresIn` timestamp, and a `UserInfo` object with the same identity details, allowing the frontend to immediately update the UI without additional API calls.