Skip to main content

Authorization

Authorization built-in Web console​

The ProconTEL Authorization feature allows customers logged in Web console to utilize existing Access Tokens obtained from our Identity Server for request authorization within their client applications. By integrating this feature, you can ensure secure and authenticated access to your application endpoints.

In order to add ProconTEL Authorization it's necessary to add the following code to your application

builder.Services.AddProconTelAuthorization(builder.Configuration);
app.UseAuthentication();
app.UseAuthorization();

Below you can find full example code with ProconTEL Authorization added.

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureProconTel(builder.Configuration);
builder.Services.AddProconTel(builder.Configuration);

builder.Services.AddProconTelAuthorization(builder.Configuration);
builder.Services.AddControllers();

var app = builder.Build();
app.UsePublicApp(builder.Configuration).AddCustomUserInterface("swagger", "/swagger");
app.UseProconTel();
app.MapProconTel();

app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

await app.RunAsync();

Authorization configuration​

Authorization uses configuration options that are set automatically when the app is started in the PCT 4 environment:

  "ProconTel": {
"App": {
"Authorization": {
"Authority": "https://procontel.local:9800",
"ValidAudiences": [ "procontel" ],
"AccessTokenCookieName": "Pct-Access-Token",
"IsHttpIdentityProviderAllowed": false
}
}
}

The following guidelines apply when modifying these settings. Most values should remain at their defaults unless explicitly required:

1.ValidAudiences: "procontel" has to remain in the audience for the app to work correctly, but more can be added so that it is possible to authenticate more audiences. Example altered value: [ "procontel", "user" ] 2.Authority: it is required to set this value in order to use methods from IJwtTokenProvider while running the app outside of ProconTEL environment.

Securing endpoints with ProconTEL Authorization enabled​

With ProconTEL Authorization configured you can simply use [Authorize] attribute on your endpoints to secure them

[Authorize]
[HttpPost]
public void Post(string value)
{
Console.WriteLine($"post {value}");
}

Furthermore it's possible to use ProconTEL Roles by adding it to Authorize attribute. This way only users with the specific role will have access to the endpoint

[Authorize(Roles = "application-access")]
[HttpGet("{id}")]
public string Get(int id)
{
return $"value {id}";
}

Authorization with External Clients (out of Web console)​

External Clients allow access to authorized endpoints without logging into the Web console. This section provides guidance on obtaining and using a JWT token for authorization. The management of External Clients, including creation, editing, obtaining secrets, and assigning roles, can be done in the 'External Clients' menu.

Authorization can be applied using both the [Authorize] attribute and the [Authorize(Roles="sample-role")] attribute.

Obtaining a JWT Token​

To acquire a JWT token, send a POST request to the token issuer with your client credentials. The issuer will authenticate the request and return a JWT token. The following code snippet demonstrates this process in C#:

var httpClient = new HttpClient();

// Fetch OpenID configuration
using var response = await httpClient.GetAsync("https://procontel.local:9800/.well-known/openid-configuration");
var openIdConfigResponse = JsonSerializer.Deserialize<OpenIdConfigResponse>(await response.Content.ReadAsStringAsync(), new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
});

var tokenEndpoint = openIdConfigResponse.TokenEndpoint;

// Prepare token request
var tokenRequest = new HttpRequestMessage();
tokenRequest.RequestUri = new Uri(tokenEndpoint);
tokenRequest.Method = HttpMethod.Post;

var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "CLIENT_ID_PLACEHOLDER"),
new KeyValuePair<string, string>("client_secret", "CLIENT_SECRET_PLACEHOLDER"),
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("scope", "procontel") // specifying scope is optional, by default it is "procontel"
};
tokenRequest.Content = new FormUrlEncodedContent(formData);

// Send token request
var tokenResponse = await httpClient.SendAsync(tokenRequest);
var tokenResult = await tokenResponse.Content.ReadAsStringAsync();
Console.WriteLine(tokenResult);

class OpenIdConfigResponse
{
[JsonPropertyName("token_endpoint")]
public string TokenEndpoint { get; set; }
}

It is also possible to obtain a JWT for external clients using a wrapper method in ProconTEL SDK:

private readonly IJwtTokenProvider _jwtTokenProvider;

public JwtTokenService(IJwtTokenProvider jwtTokenProvider)
{
_jwtTokenProvider = jwtTokenProvider;
}

public async Task<string> GetJwtToken()
{
var token = await _jwtTokenProvider.GetJwtTokenForExternalClientAsync("CLIENT_ID_PLACEHOLDER", "CLIENT_SECRET_PLACEHOLDER");
return token;
}

Replace "CLIENT_ID_PLACEHOLDER" with your External ClientId and "CLIENT_SECRET_PLACEHOLDER" with your External Client secret.

Using a JWT Token​

Once you have obtained the JWT token, you can use it for further authorization or parse it to examine its claims. The following code demonstrates how to parse a JWT token using the System.IdentityModel.Tokens.Jwt package in .NET:

using System.IdentityModel.Tokens.Jwt;

var jwtHandler = new JwtSecurityTokenHandler();
var token = jwtHandler.ReadJwtToken(jwtAsString);

var expiration = token.ValidTo;

To check a specific claim, you can use the Claims property of the JwtSecurityToken object:

var isInRole = token.Claims.Any(c => c.Type == "role" && c.Value == "admin");

Using the JWT Token for Authorization​

To authenticate subsequent HTTP requests using the JWT token, include it in the Authorization header as a Bearer token. The server will use the JWT token to identify the user or application. Here's an example of making a GET request with a Bearer token using HttpClient in C#:

...
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, uriString);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);

if (payload != null)
{
requestMessage.Content = SetHttpContent(payload);
}

var response = await httpClient.SendAsync(requestMessage);
...

Make sure to replace jwtToken with your actual JWT token and uriString with the URI of the resource you are requesting. If there is any payload (data) to be sent with the request, it can be added using the SetHttpContent method according to your application's needs.

Authorization with user credentials​

There are 2 ways to obtain a JWT token using login credentials.

GetJwtTokenAsync method​

It is possible to request JWT tokens using login credentials using an ProconTEL SDK GetJwtTokenAsync method. It returns a pair of an access token and a refresh token (if available). Lifetime of both is 1 hour.

private readonly IJwtTokenProvider _jwtTokenProvider;

public JwtTokenService(IJwtTokenProvider jwtTokenProvider)
{
_jwtTokenProvider = jwtTokenProvider;
}

public async Task<string> GetJwtToken()
{
var tokenPair = await _jwtTokenProvider.GetJwtTokenForUserAsync("username", "password");
return tokenPair.AccessToken;
}

Obtaining token via request​

var httpClient = new HttpClient();

// Fetch OpenID configuration
using var response = await httpClient.GetAsync("https://procontel.local:9800/.well-known/openid-configuration");
var openIdConfigResponse = JsonSerializer.Deserialize<OpenIdConfigResponse>(await response.Content.ReadAsStringAsync(), new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
});

var tokenEndpoint = openIdConfigResponse.TokenEndpoint;

// Prepare token request
var tokenRequest = new HttpRequestMessage();
tokenRequest.RequestUri = new Uri(tokenEndpoint);
tokenRequest.Method = HttpMethod.Post;

var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "pct4client"),
new KeyValuePair<string, string>("username", "USERNAME_PLACEHOLDER"),
new KeyValuePair<string, string>("password", "PASSWORD_PLACEHOLDER"),
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("client_secret_", "secret"), // optional field
new KeyValuePair<string, string>("scope", "procontel") // Specifying scope is optional, by default it is "offline_access openid procontel" when using internal idenity provider. For Keycloak is depends on the Keycloak settings.
};
tokenRequest.Content = new FormUrlEncodedContent(formData);

// Send token request
var tokenResponse = await httpClient.SendAsync(tokenRequest);
var tokenResult = await tokenResponse.Content.ReadAsStringAsync();
Console.WriteLine(tokenResult);

class OpenIdConfigResponse
{
[JsonPropertyName("token_endpoint")]
public string TokenEndpoint { get; set; }
}

Replace "USERNAME_PLACEHOLDER" with your username and "PASSWORD_PLACEHOLDER" with your password.

Authorization using refresh token​

Obtaining refresh token​

Obtaining a refresh token is possible when requesting a JWT token using client credentials when scope includes "offline_access" (by default it is included). It is not possible to get a refresh token using external client credentials.

Here is demonstrated how to do it using GetJwtTokenForUserAsync() method, however, it is also possible to retrieve a refresh token via direct request as described in the prior section, tokenResult should contain it.

private readonly IJwtTokenProvider _jwtTokenProvider;

public JwtTokenService(IJwtTokenProvider jwtTokenProvider)
{
_jwtTokenProvider = jwtTokenProvider;
}

public async Task<string> GetRefreshToken()
{
var tokenPair = await _jwtTokenProvider.GetJwtTokenForUserAsync("username", "password");
return tokenPair.RefreshToken;
}

Using the refresh token​

It is possible to use refresh token to obtain an access token. The response contains a new refresh token as well. Refresh token obtained from internal identity provider can only be used once.

private readonly IJwtTokenProvider _jwtTokenProvider;

public JwtTokenService(IJwtTokenProvider jwtTokenProvider)
{
_jwtTokenProvider = jwtTokenProvider;
}

public async Task<string> GetAccessToken()
{
var tokenPair = await _jwtTokenProvider.GetJwtTokenWithRefreshTokenAsync("refresh_token");
return tokenPair.AccessToken;
}

It is also possible to obtain an access token via request:

var httpClient = new HttpClient();

// Fetch OpenID configuration
using var response = await httpClient.GetAsync("https://procontel.local:9800/.well-known/openid-configuration");
var openIdConfigResponse = JsonSerializer.Deserialize<OpenIdConfigResponse>(await response.Content.ReadAsStringAsync(), new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
});

var tokenEndpoint = openIdConfigResponse.TokenEndpoint;

// Prepare token request
var tokenRequest = new HttpRequestMessage();
tokenRequest.RequestUri = new Uri(tokenEndpoint);
tokenRequest.Method = HttpMethod.Post;

var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "pct4client"), // the value can be different but has to match client_id when the refresh token was requested
new KeyValuePair<string, string>("refresh_token", "REFRESH_TOKEN_PLACEHOLDER"),
new KeyValuePair<string, string>("grant_type", "refresh_token"),
};
tokenRequest.Content = new FormUrlEncodedContent(formData);

// Send token request
using var tokenResponse = await httpClient.SendAsync(tokenRequest);
var tokenResult = await tokenResponse.Content.ReadAsStringAsync();
Console.WriteLine(tokenResult);

class OpenIdConfigResponse
{
[JsonPropertyName("token_endpoint")]
public string TokenEndpoint { get; set; }
}

Refresh token revocation​

IJwtTokenProvider interface can be used to revoke a refresh token:

private readonly IJwtTokenProvider _jwtTokenProvider;

public JwtTokenService(IJwtTokenProvider jwtTokenProvider)
{
_jwtTokenProvider = jwtTokenProvider;
}

public async Task<bool> RevokeToken()
{
var result = await _jwtTokenProvider.RevokeRefreshTokenAsync("refresh_token");
return result;
}

It can also be done via request:

var httpClient = new HttpClient();

var tokenRequest = new HttpRequestMessage(HttpMethod.Post, $"https://procontel.local:9800/connect/revocation");

var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "pct4client"), // the value can be different but has to match client_id when the refresh token was requested
new KeyValuePair<string, string>("token", "REFRESH_TOKEN_PLACEHOLDER"),
new KeyValuePair<string, string>("grant_type", "refresh_token"),
};
tokenRequest.Content = new FormUrlEncodedContent(formData);

// Send token request
using var tokenResponse = await httpClient.SendAsync(tokenRequest);
var tokenResult = await tokenResponse.Content.ReadAsStringAsync();
Console.WriteLine(tokenResult);

class OpenIdConfigResponse
{
[JsonPropertyName("token_endpoint")]
public string TokenEndpoint { get; set; }
}