microsoft/migrating-owin-oauth-to-jwt
> Migrates legacy OWIN OAuth bearer authentication (Microsoft.Owin.Security.OAuth) to ASP.NET Core JWT Bearer authentication (Microsoft.AspNetCore.Authentication.JwtBearer). Use ONLY when Microsoft.Owin.Security.OAuth has been flagged as obsolete or deprecated and must be replaced — not for version-bump scenarios where the OWIN package is still supported.
npx skills add https://github.com/microsoft/upgrade-agent-plugins --skill migrating-owin-oauth-to-jwt
Migrate OAuth bearer token authentication from OWIN (Microsoft.Owin.Security.OAuth) to ASP.NET Core JWT Bearer (Microsoft.AspNetCore.Authentication.JwtBearer). ASP.NET Core has no built-in equivalent to OWIN's OAuthAuthorizationServerProvider for issuing tokens — token issuance must move to an external identity provider such as Duende IdentityServer or Azure AD / Microsoft Entra ID. Token validation is handled by AddJwtBearer() with TokenValidationParameters.
> Related skills: For OWIN cookie authentication migration, see migrating-owin-cookie-auth. For general OWIN middleware migration, see migrating-owin-to-aspnet-core. For Azure AD authentication library migration, see migrating-adal-to-msal.
<PackageReference Include="Microsoft.Owin.Security.OAuth" Version="4.x.x" />
<PackageReference Include="Microsoft.Owin.Security" Version="4.x.x" />
<PackageReference Include="Microsoft.Owin" Version="4.x.x" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="{version}" />
Use tools or NuGet to find the latest stable version matching the target framework.
Migration Progress:
- [ ] Step 1: Audit OWIN OAuth usage
- [ ] Step 2: Determine token issuance strategy
- [ ] Step 3: Update package references
- [ ] Step 4: Replace bearer token validation
- [ ] Step 5: Migrate token issuance (if applicable)
- [ ] Step 6: Update authorization attributes
- [ ] Step 7: Build and verify
Scan the project for:
using Microsoft.Owin.Security.OAuth; statementsapp.UseOAuthBearerAuthentication(...) calls (token validation)app.UseOAuthAuthorizationServer(...) calls (token issuance)OAuthAuthorizationServerProvider subclasses (custom token generation)OAuthAuthorizationServerOptions configuration (token endpoint, expiry, etc.)Categorize findings into two buckets:
If the project issues tokens via OAuthAuthorizationServerProvider, choose a replacement:
| Strategy | When to Use |
|----------|-------------|
| Azure AD / Entra ID | Already using Azure; want managed identity provider |
| Duende IdentityServer | Need self-hosted OAuth 2.0 / OpenID Connect server |
| Custom JWT generation | Simple scenarios; use System.IdentityModel.Tokens.Jwt to create tokens manually |
If the project only validates tokens, skip to Step 4.
Remove OWIN OAuth packages and add the JWT Bearer package (see "Package Reference Changes" above). Update using directives:
// Old
using Microsoft.Owin.Security.OAuth;
// New
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
The OWIN bearer middleware becomes ASP.NET Core service configuration:
// OWIN
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AccessTokenFormat = new TicketDataFormat(new MachineKeyDataProtector("OAuth"))
});
// ASP.NET Core
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://login.microsoftonline.com/{tenant}/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = "{client-id}",
ValidIssuer = "https://login.microsoftonline.com/{tenant}/v2.0"
};
});
// In the middleware pipeline
app.UseAuthentication();
app.UseAuthorization();
| OWIN Option | ASP.NET Core Equivalent | Notes |
|-------------|------------------------|-------|
| OAuthBearerAuthenticationOptions | JwtBearerOptions | Configured via AddJwtBearer() |
| AccessTokenFormat | options.TokenValidationParameters | JWT validation replaces data protection format |
| Provider.OnValidateIdentity | options.Events.OnTokenValidated | Event-based model |
| Provider.OnRequestToken | options.Events.OnMessageReceived | Customize token extraction |
| AllowedAudiences | options.TokenValidationParameters.ValidAudiences | Array of accepted audiences |
If the OWIN app used OAuthAuthorizationServerProvider to issue tokens, that logic must be extracted into a separate identity provider or a custom token endpoint.
Option A: Custom JWT token endpoint (for simple scenarios):
app.MapPost("/token", async (LoginRequest request, IConfiguration config) =>
{
// Validate credentials (replace with actual validation)
if (!await ValidateCredentialsAsync(request.Username, request.Password))
return Results.Unauthorized();
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Key"]));
var token = new JwtSecurityToken(
issuer: config["Jwt:Issuer"],
audience: config["Jwt:Audience"],
claims: new[] { new Claim(ClaimTypes.Name, request.Username) },
expires: DateTime.UtcNow.AddHours(1),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
return Results.Ok(new { access_token = new JwtSecurityTokenHandler().WriteToken(token) });
});
Option B: External identity provider — migrate the token issuance logic to Duende IdentityServer or Azure AD / Entra ID and configure the API to validate tokens from that provider.
OWIN OAuth typically used [Authorize] attributes that work with the OWIN authentication type. In ASP.NET Core, ensure the authorization scheme matches:
// If using a single scheme (default), [Authorize] works as-is
[Authorize]
public class SecureController : ControllerBase { }
// If using multiple schemes, specify explicitly
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ApiController : ControllerBase { }
dotnet build
HttpContext.UserCheck that Authority, ValidAudience, and ValidIssuer match the token issuer's configuration. Use jwt.ms to decode the token and inspect the iss and aud claims.
ASP.NET Core intentionally removed the built-in OAuth authorization server. For production scenarios, use Duende IdentityServer or Azure AD / Entra ID rather than hand-rolling a token endpoint. A custom endpoint (Step 5, Option A) is suitable only for simple internal scenarios.
OWIN's default token format used machine key data protection, which is incompatible with JWT. Existing tokens issued by the OWIN server will not validate against AddJwtBearer(). Plan a token rollover: deploy the new JWT-based system, then expire or revoke old tokens.
ASP.NET Core maps JWT claims differently than OWIN by default. If claims like sub or name are missing, configure TokenValidationParameters.NameClaimType and RoleClaimType, or disable automatic mapping:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
Take microsoft/migrating-owin-oauth-to-jwt from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.