Implementing OAuth Token and Refresh Token in .NET Web Application
OAuth 2.0 is a popular authorization framework that enables third-party applications to obtain limited access to a web service. The framework uses access tokens to prove an identity between consumers and service providers. This blog will guide you through the implementation of OAuth token and refresh token handling in a .NET Web Application.
Embark on a journey of continuous learning and exploration with DotNet-FullStack-Dev. Uncover more by visiting our https://dotnet-fullstack-dev.blogspot.com reach out for further information.
What is OAuth?
OAuth 2.0 is an open standard for token-based authentication and authorization on the internet. It allows an end user’s account information to be used by third-party services without exposing the user’s password.
Why Use OAuth?
- Security: It allows limited access to a user’s data without exposing their password.
- User Experience: It allows users to authorize third-party applications without needing to create a new account.
- Scalability: It is widely adopted and supported, making it easier to integrate with various services.
Understanding Access Tokens and Refresh Tokens
- Access Token: A short-lived token used to access protected resources.
- Refresh Token: A long-lived token used to obtain a new access token once the current access token expires.
Implementing OAuth in a .NET Web Application
Step 1: Setting Up the OAuth Server
We’ll use IdentityServer4, a popular OpenID Connect and OAuth 2.0 framework for ASP.NET Core, to set up the OAuth server.
Install the necessary packages:
dotnet add package IdentityServer4
dotnet add package IdentityServer4.AspNetIdentity
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
Configure IdentityServer:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
}
}
Configure Clients and API Resources:
public static class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "api1" },
AllowOfflineAccess = true
}
};
}
}
Step 2: Implementing OAuth in the Client Application
Install the necessary packages:
dotnet add package IdentityModel
dotnet add package Microsoft.Extensions.Http
Get Access Token and Refresh Token:
public async Task<TokenResponse> GetTokenAsync(string username, string password)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
if (disco.IsError) throw new Exception(disco.Error);
var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
UserName = username,
Password = password,
Scope = "api1 offline_access"
});
if (tokenResponse.IsError) throw new Exception(tokenResponse.Error);
return tokenResponse;
}
Refresh Access Token:
public async Task<TokenResponse> RefreshTokenAsync(string refreshToken)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
if (disco.IsError) throw new Exception(disco.Error);
var tokenResponse = await client.RequestRefreshTokenAsync(new RefreshTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
RefreshToken = refreshToken
});
if (tokenResponse.IsError) throw new Exception(tokenResponse.Error);
return tokenResponse;
}
Step 3: Using the Tokens in the Client Application
Store Tokens:
private void StoreTokens(TokenResponse tokenResponse)
{
var accessToken = tokenResponse.AccessToken;
var refreshToken = tokenResponse.RefreshToken;
// Store tokens securely
// e.g., in a secure storage or database
}
Use Access Token to Call Protected API:
public async Task CallApiAsync(string accessToken)
{
var client = new HttpClient();
client.SetBearerToken(accessToken);
var response = await client.GetAsync("https://localhost:5001/api/protected");
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.ReasonPhrase);
}
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
Conclusion
OAuth 2.0 provides a secure way to authorize and authenticate users without exposing sensitive credentials. Using access tokens and refresh tokens, we can implement a robust authentication mechanism in our .NET web applications. IdentityServer4 simplifies the process of setting up an OAuth server, and integrating it with a client application involves obtaining and refreshing tokens to access protected resources securely.