Fake Identity Provider Feature Documentation¶
A lightweight OAuth2/OpenID Connect identity provider supporting multiple grant types and hardcoded users, designed for development and testing scenarios.
- Fake Identity Provider Feature Documentation
- Bare-Bones Identity Provider Documentation
Features¶
- OAuth2 and OpenID Connect support
- Public and confidential client support
- Multiple grant types (authorization code, password, client credentials, refresh token)
- JWT token generation
- Debug endpoints for development
- No database required
- Built-in user selection interface
- CORS support
- Client redirect URI validation
- Support for SPA, server, and API tool clients
- Cookie-based Single Sign-On (SSO) across browser tabs
Setup¶
1. Install Package¶
<PackageReference Include="BridgingIT.DevKit.Presentation.Web" Version="x.y.z" />
builder.Services.AddFakeIdentityProvider(options => {
options.Enabled(builder.Environment.IsDevelopment())
.WithIssuer("https://localhost:5001")
.WithUsers(Fakes.Users)
.WithTokenLifetimes(
accessToken: TimeSpan.FromMinutes(30),
refreshToken: TimeSpan.FromDays(1))
.EnableCookieSingleSignOn() // Optional; enabled by default
.EnablePersistentRefreshTokens() // Required for cookie SSO; enabled by default
// Public Client (SPA)
.WithClient(
"spa-client",
"Angular SPA",
"http://localhost:4200/callback")
// Blazor WASM (Public Client)
.WithClient(
"blazor-wasm",
"Blazor WASM Frontend",
"https://localhost:5001/authentication/login-callback",
"https://localhost:5001/authentication/logout-callback")
// Confidential Client (Server)
.WithConfidentalClient(
"mvc-app",
"MVC Server",
"mvc-secret",
["https://localhost:5002/signin-oidc"])
// Blazor Server (Confidential Client)
.WithConfidentalClient(
"blazor-server",
"Blazor Server Frontend",
"server-secret",
["https://localhost:5003/signin-oidc"])
// WebAPI Backend
.WithConfidentalClient(
"api-backend",
"API Backend",
"api-secret",
["https://localhost:5001"])
// API Tools
.WithClient(
"swagger",
"Swagger UI",
"https://localhost:5001/swagger/oauth2-redirect.html");
});
3. Define Users¶
public static class Fakes
{
public static readonly FakeUser[] Users = [
new("luke.skywalker@starwars.com", "Luke Skywalker",
[Role.Administrators, Role.Users], isDefault: true),
new("yoda@starwars.com", "Yoda",
[Role.Administrators])
// ...... Add more users
];
}
Token Types¶
Access Token¶
JSON Web Token (JWT) containing: - User identity (sub, email) - User info (name, roles) - Token metadata (iss, aud, exp) - Scope permissions
ID Token¶
OpenID Connect token with: - Required claims (iss, sub, aud, exp) - Profile data (name, email) - Role information
Refresh Token¶
Long-lived token for obtaining new access tokens.
Authentication Flows¶
Public Client Flow (SPA, Mobile)¶
sequenceDiagram
participant Client
participant IDP
participant User
Client->>IDP: GET /authorize
alt User has auth cookie (SSO)
IDP->>Client: Code (no login page)
else First visit or cookie expired
IDP->>User: Show login page
User->>IDP: Select user
IDP->>Client: Code + Set auth cookie
end
Client->>IDP: POST /token
Note over Client,IDP: Exchange code for tokens
IDP->>Client: Access + Refresh tokens
Client->>IDP: GET /userinfo
IDP->>Client: User data
Key points: - No client secret - State parameter required - Access token in Authorization header - Refresh token for token renewal
Confidential Client Flow (Server Apps)¶
sequenceDiagram
participant Client
participant IDP
participant User
Client->>IDP: GET /authorize
alt User has auth cookie (SSO)
IDP->>Client: Code (no login page)
else First visit or cookie expired
IDP->>User: Show login page
User->>IDP: Select user
IDP->>Client: Code + Set auth cookie
end
Client->>IDP: POST /token
Note over Client,IDP: With client_secret
IDP->>Client: Access + Refresh tokens
Client->>IDP: GET /userinfo
IDP->>Client: User data
Key points: - Client secret required - Secure token storage - Server-side token management - Valid redirect URIs enforced
Cookie Single Sign-On (SSO)¶
When enabled (default), the fake identity provider sets an HTTP-only authentication cookie during the first authorization code flow. On subsequent /authorize requests, the provider checks this cookie and immediately redirects with a new authorization code if the user is still authenticated, skipping the user selection page.
How it Works¶
sequenceDiagram
participant Tab1 as Browser Tab 1
participant Tab2 as Browser Tab 2
participant IDP as Identity Provider
Tab1->>IDP: GET /authorize
IDP->>Tab1: Show login page
Tab1->>IDP: Select user
IDP->>Tab1: Code + Set auth cookie
Tab1->>IDP: POST /token (exchange code)
IDP->>Tab1: Access + Refresh tokens
Note over Tab2,IDP: Open app in new tab
Tab2->>IDP: GET /authorize (with cookie)
IDP->>IDP: Validate cookie & user
IDP->>Tab2: Code (no login page)
Tab2->>IDP: POST /token (exchange code)
IDP->>Tab2: Access + Refresh tokens
Configuration¶
| Option | Default | Description |
|---|---|---|
EnableCookieSingleSignOn |
true |
When true, the authorize endpoint checks for an existing auth cookie and skips the login page for already-authenticated users. |
EnablePersistentRefreshTokens |
true |
Must be true for cookie SSO to work; this sets the auth cookie during token exchange. |
Opting Out¶
To disable cookie SSO and always show the login page:
builder.Services.AddFakeIdentityProvider(options =>
{
options.Enabled(builder.Environment.IsDevelopment())
.EnableCookieSingleSignOn(false)
.WithUsers(Fakes.Users)
// ...
});
Security Notes¶
- The authorize endpoint still validates
client_idandredirect_uribefore the SSO redirect, even when a valid cookie is present. - The cookie is HTTP-only (
options.Cookie.HttpOnly = true) and Secure (options.Cookie.SecurePolicy = CookieSecurePolicy.Always). - The cookie inherits the refresh token lifetime (default 1 day).
Client Integration Examples¶
1. Angular (Public Client)¶
import { AuthConfig } from 'angular-oauth2-oidc';
export const authConfig: AuthConfig = {
issuer: 'http://localhost:5000',
redirectUri: window.location.origin + '/callback',
clientId: 'spa-client',
scope: 'openid profile email roles'
};
2. ASP.NET Core MVC (Confidential Client)¶
builder.Services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options => {
options.Authority = "http://localhost:5000";
options.ClientId = "mvc-app";
options.ClientSecret = "mvc-secret";
options.ResponseType = "code";
options.SaveTokens = true;
});
3. Blazor WebAssembly¶
// Client Program.cs
builder.Services.AddOidcAuthentication(options =>
{
options.ProviderOptions.Authority = "https://localhost:5001";
options.ProviderOptions.ClientId = "blazor-wasm";
options.ProviderOptions.DefaultScopes.Add("roles");
options.ProviderOptions.ResponseType = "code";
options.ProviderOptions.PostLogoutRedirectUri = "authentication/logout-callback";
options.ProviderOptions.RedirectUri = "authentication/login-callback";
});
// Client App.razor
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
</AuthorizeRouteView>
</Found>
</Router>
</CascadingAuthenticationState>
4. WebAPI Backend¶
// Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = "api",
ValidateIssuer = true,
ValidIssuer = "https://localhost:5001"
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdminRole", policy =>
policy.RequireRole("Administrators"));
});
// WeatherForecastController.cs
[ApiController]
[Route("[controller]")]
[Authorize]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var user = User.Identity.Name;
// Implementation
}
[HttpPost]
[Authorize(Policy = "RequireAdminRole")]
public IActionResult Create(WeatherForecast forecast)
{
// Implementation
}
}
API Reference¶
Authorization Endpoint¶
Start OAuth2 flow and user selection.
GET /api/_system/identity/connect/authorize
Parameters:
- response_type: "code"
- client_id: Client identifier
- redirect_uri: Return URL
- scope: Requested permissions
- state: Security token
Token Endpoint¶
Issue tokens using various grant types.
POST /api/_system/identity/connect/token
Content-Type: application/x-www-form-urlencoded
Grant Types: 1. Authorization Code
grant_type=authorization_code
&client_id=client_id
&code=auth_code
&redirect_uri=callback_url
-
Password Grant
grant_type=password &client_id=client_id &username=user@example.com &scope=openid profile -
Client Credentials
grant_type=client_credentials &client_id=client_id &scope=api -
Refresh Token
grant_type=refresh_token &client_id=client_id &refresh_token=token
Response:
{
"access_token": "eyJhbGci...",
"expires_in": 1800,
"refresh_token": "eyJhbGci...",
"token_type": "Bearer",
"scope": "openid profile",
"id_token": "eyJhbGci..."
}
UserInfo Endpoint¶
Get authenticated user data.
GET /api/_system/identity/connect/userinfo
Authorization: Bearer token
Response:
{
"sub": "user_id",
"name": "User Name",
"email": "user@example.com",
"roles": ["Admin", "User"]
}
Debug Endpoint¶
Development information about configuration.
GET /api/_system/identity/connect/debuginfo
Response:
{
"tokenIssuer": "https://localhost:5001",
"configuredClients": [
{
"clientId": "spa-client",
"name": "SPA App",
"redirectUris": ["http://localhost:4200/callback"]
}
],
"configuredUsers": [
{
"email": "user@example.com",
"name": "User Name",
"roles": ["Admin"]
}
]
}
Well-Known Configuration¶
OpenID Connect discovery document.
GET /api/_system/identity/.well-known/openid-configuration
Response:
{
"issuer": "https://localhost:5001",
"authorization_endpoint": "https://localhost:5001/api/_system/identity/connect/authorize",
"token_endpoint": "https://localhost:5001/api/_system/identity/connect/token",
"userinfo_endpoint": "https://localhost:5001/api/_system/identity/connect/userinfo",
"end_session_endpoint": "https://localhost:5001/api/_system/identity/connect/logout"
}
Error Handling¶
Standard OAuth2 error responses:
{
"error": "invalid_request",
"error_description": "Error details"
}
Common error codes: - invalid_request - invalid_client - invalid_grant - unauthorized_client - unsupported_grant_type
Security Considerations¶
Development Use Only¶
- Designed for development and testing
- No production security measures
- Hardcoded users and clients
- No token encryption requirements
Default Behaviors¶
- Client secrets not validated
- Access tokens never expire by default
- No real user authentication
- CORS enabled for all origins
- Debug endpoints exposed
Cookie Single Sign-On¶
- Cookie SSO is enabled by default for development convenience
- The auth cookie is scoped to the IDP origin and shared across browser tabs
- Client validation (
client_id,redirect_uri) still applies to SSO redirects - For testing scenarios where each request should show the login page, disable with
EnableCookieSingleSignOn(false)
Development Tips¶
Custom Client Setup¶
// Multiple redirect URIs
.WithClient(
"angular-app",
"Angular Frontend",
["http://localhost:4200/callback",
"http://localhost:4200/silent-refresh"])
// API documentation tools
.WithClient(
"swagger",
"Swagger UI",
"https://localhost:5001/swagger/oauth2-redirect.html")
Testing Scenarios¶
- Multiple users
- Role-based access
- Token validation
- Authentication flows
- Client registrations
Related Resources¶
- OAuth 2.0 RFC
- OpenID Connect Specification
- JWT Documentation
Bare-Bones Identity Provider Documentation¶
A lightweight OAuth2/OpenID Connect identity provider supporting multiple grant types and hardcoded users. Perfect for development and testing scenarios where a full-fledged identity provider would be overkill.
Features¶
- OAuth2 and OpenID Connect support
- Multiple grant types (authorization code, password, client credentials, refresh token)
- Hardcoded user management
- JWT token generation
- No database required
- Minimal setup
Setup¶
1. Install Package¶
<PackageReference Include="BridgingIT.DevKit.Presentation.Web" Version="1.0.0" />
2. Configure Services¶
In your Program.cs:
builder.Services.AddFakeIdentityProvider(options =>
{
options.Users = Fakes.Users; // Your hardcoded users
options.TokenIssuer = "http://localhost:5000"; // Your IDP base URL
options.GroupPrefix = "/api/_system/identity"; // Base route for all endpoints
});
3. Define Users¶
public static class Fakes
{
public static readonly FakeUser[] Users =
[
new("luke.skywalker@starwars.com", "Luke Skywalker",
[Role.Administrators, Role.Users], isDefault: true),
new("yoda@starwars.com", "Yoda",
[Role.Administrators])
];
}
Client Integration Examples¶
1. Angular Application (Authorization Code Flow)¶
Configure your Angular application with OIDC client library:
import { AuthConfig } from 'angular-oauth2-oidc';
export const authConfig: AuthConfig = {
issuer: 'http://localhost:5000',
redirectUri: window.location.origin + '/callback',
clientId: 'any_client_id',
scope: 'openid profile email roles',
responseType: 'code'
};
Authorization Code Flow¶
sequenceDiagram
participant Browser
participant App
participant IDP
App->>Browser: 1. Redirect to /connect/authorize
Browser->>IDP: 2. GET /connect/authorize
IDP->>Browser: 3. Show user selection page
Browser->>Browser: 4. Select user
Browser->>IDP: 5. GET /connect/authorize/callback
IDP->>Browser: 6. Redirect with code
Browser->>App: 7. Pass authorization code
App->>IDP: 8. POST /connect/token
Note over App,IDP: Exchange code for tokens
IDP->>App: 9. Return tokens (access + refresh)
App->>IDP: 10. GET /connect/userinfo
Note over App,IDP: Get user data with access token
IDP->>App: 11. Return user info
2. Direct API Access (Password Grant)¶
POST /api/_system/identity/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=password
&client_id=any_client_id
&username=luke.skywalker@starwars.com
&scope=openid profile email roles
3. Service-to-Service (Client Credentials)¶
POST /api/_system/identity/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=any_client_id
&scope=api
Note: This IDP implementation does not validate client_ids. Any string can be used as client_id in the requests.
API Reference¶
Authorization Endpoint¶
Starts the OAuth2 authorization flow with user selection.
GET /api/_system/identity/connect/authorize?
response_type=code
&client_id=any_client_id
&redirect_uri=http://localhost:4200/callback
&scope=openid profile email roles
&state=abc123
Token Endpoint¶
Issues tokens using various grant types.
Authorization Code Grant¶
POST /api/_system/identity/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=any_client_id
&code=xyz789
&redirect_uri=http://localhost:4200/callback
Sample Response:
{
"access_token": "eyJhbGci...",
"expires_in": 1800,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGci...",
"token_type": "Bearer",
"scope": "openid email profile roles",
"session_state": "5adfe176-2f18-4803-a0b3-21c678c49b5b"
}
UserInfo Endpoint¶
Returns information about the authenticated user.
GET /api/_system/identity/connect/userinfo
Authorization: Bearer eyJhbGci...
Response:
{
"sub": "749ecbc50c2364add0caa40f9afc2bbf",
"name": "Luke Skywalker",
"given_name": "Luke",
"family_name": "Skywalker",
"preferred_username": "luke.skywalker@starwars.com",
"email": "luke.skywalker@starwars.com",
"email_verified": true,
"roles": ["Administrators", "Users"]
}
OpenID Configuration¶
Returns the OpenID Connect discovery document.
GET /api/_system/identity/.well-known/openid-configuration
Response:
{
"issuer": "http://localhost:5000",
"authorization_endpoint": "http://localhost:5000/api/_system/identity/connect/authorize",
"token_endpoint": "http://localhost:5000/api/_system/identity/connect/token",
"userinfo_endpoint": "http://localhost:5000/api/_system/identity/connect/userinfo",
"end_session_endpoint": "http://localhost:5000/api/_system/identity/connect/logout",
"grant_types_supported": [
"authorization_code",
"password",
"client_credentials",
"refresh_token"
],
"response_types_supported": ["code"],
"scopes_supported": ["openid", "profile", "email", "roles"],
"claims_supported": [
"sub",
"name",
"family_name",
"given_name",
"preferred_username",
"email",
"email_verified"
]
}
Logout Endpoint¶
Handles user logout with optional redirect.
POST /api/_system/identity/connect/logout?
post_logout_redirect_uri=http://localhost:4200
&state=abc123
Error Handling¶
All endpoints return OAuth2 compliant error responses:
{
"error": "invalid_request",
"error_description": "The request is missing a required parameter"
}
Common error codes: - invalid_request - invalid_client - invalid_grant - unauthorized_client - unsupported_grant_type - invalid_scope
Disclaimer¶
This Identity Provider is designed exclusively for development and testing environments, with intentionally simplified security measures that make it unsuitable for production use. It provides basic OAuth2 and OpenID Connect flows without rigorous security validation. User credentials are stored in plaintext, authentication is simplified, and token validation is minimal. Security features like rate limiting, audit logging, and proper session management are omitted for development convenience.
The provider includes development-friendly features like debug endpoints and permissive CORS policies that would pose security risks in production. Use this provider only in controlled development environments, ideally on localhost or protected development networks. Never expose it to public networks, use it with production data, or connect it to production services. For production deployments, always use a properly secured identity provider.
- This IDP is designed for development and testing
- Uses symmetric key signing (HS256)
- No real user authentication
- No real client secret validation