diff --git a/ExampleApplication/ExampleApplication.csproj b/ExampleApplication/ExampleApplication.csproj
index 40b1231..cb449d0 100644
--- a/ExampleApplication/ExampleApplication.csproj
+++ b/ExampleApplication/ExampleApplication.csproj
@@ -2,11 +2,15 @@
Exe
- netcoreapp3.1
+ net6.0
+
+
+
+
diff --git a/ExampleApplication/Program.cs b/ExampleApplication/Program.cs
index efbd385..90d184e 100644
--- a/ExampleApplication/Program.cs
+++ b/ExampleApplication/Program.cs
@@ -1,10 +1,15 @@
using System;
+using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
+using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Ks.Fiks.Maskinporten.Client;
+using Microsoft.IdentityModel.Tokens;
namespace ExampleApplication
{
@@ -16,48 +21,87 @@ namespace ExampleApplication
*/
public class Program
{
- public static void Main(string[] args)
+ public static async Task Main(string[] args)
+ {
+ await CertExample();
+ await RsaExample();
+ }
+
+ private static async Task RsaExample()
+ {
+ // The issuer as defined in Maskinporten
+ var issuer = Environment.GetEnvironmentVariable("MASKINPORTEN_ISSUER");
+
+ // Content of PEM file containing public/private key with ----BEGIN.. and ----END removed, e.g., "MIIE...A=="
+ var pem = Environment.GetEnvironmentVariable("pem")!;
+ var rsa = RSA.Create();
+
+ //Will import both public and private part in same RSA, could be done in two different variables
+ rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(pem), out _);
+
+ const string keyIdentifier = "23b3f45a-be84-43c4-a654-2182ff14dc40";
+ var configuration = MaskinportenClientConfigurationFactory.CreateVer2Configuration(
+ issuer,
+ privateKey: rsa,
+ publicKey: rsa,
+ keyIdentifier: keyIdentifier
+ );
+
+ var maskinportenClient = new MaskinportenClient(configuration);
+ var token = await maskinportenClient.GetAccessToken("ks:fiks");
+
+ await ValidateToken(token);
+ }
+
+ private static async Task CertExample()
{
// Relative or absolute path to the *.p12-file containing the test certificate
var p12Filename = Environment.GetEnvironmentVariable("P12FILENAME");
-
+
// Password required to use the certificate
var p12Password = Environment.GetEnvironmentVariable("P12PWD");
-
+
// The issuer as defined in Maskinporten
var issuer = Environment.GetEnvironmentVariable("MASKINPORTEN_ISSUER");
var cert = new X509Certificate2(p12Filename, p12Password);
var configuration = new MaskinportenClientConfiguration(
- audience: @"https://ver2.maskinporten.no/", // ID-porten audience path
- tokenEndpoint: @"https://ver2.maskinporten.no/token", // ID-porten token path
- issuer: issuer, // Issuer name
- numberOfSecondsLeftBeforeExpire: 10, // The token will be refreshed 10 seconds before it expires
- certificate: cert);
+ @"https://ver2.maskinporten.no/", // ID-porten audience path
+ @"https://ver2.maskinporten.no/token", // ID-porten token path
+ issuer, // Issuer name
+ 10, // The token will be refreshed 10 seconds before it expires
+ cert);
var maskinportenClient = new MaskinportenClient(configuration);
- var tokenTask = maskinportenClient.GetAccessToken("ks:fiks").ContinueWith(t =>
- {
- var token = t.Result;
- Console.Out.WriteLine($"Token (expiring: {token.IsExpiring()}): {token.Token}");
+ var token = await maskinportenClient.GetAccessToken("ks:fiks");
+ await ValidateToken(token);
+ }
- return DecodeToken(token, cert);
- });
- // Do something with the token. In this case we only wait for it to be decoded and written to the console
- tokenTask.GetAwaiter().GetResult();
+ private static async Task ValidateToken(MaskinportenToken token)
+ {
+ using var client = new HttpClient();
+ var json = await client.GetStringAsync("https://ver2.maskinporten.no/jwk");
+ var jwks = new JsonWebKeySet(json);
+ var jwk = jwks.Keys.First();
+ var validationParameters = new TokenValidationParameters
+ {
+ ValidateIssuer = true,
+ ValidateAudience = false,
+ ValidateLifetime = true,
+ ValidateIssuerSigningKey = true,
+ ValidIssuer = "https://ver2.maskinporten.no/",
+ IssuerSigningKey = jwk
+ };
- }
+ var handler = new JwtSecurityTokenHandler();
+ var claimsPrincipal = handler.ValidateToken(token.Token, validationParameters, out var validatedToken);
- private static string DecodeToken(MaskinportenToken token, X509Certificate2 pubprivCertificate)
- {
- var serializer = new JsonNetSerializer();
- var provider = new UtcDateTimeProvider();
+ var claims = claimsPrincipal.Claims;
+ foreach (var claim in claims) Console.WriteLine($"{claim.Type}: {claim.Value}");
- var jwtDecoder = new JwtDecoder(serializer, new JwtValidator(serializer, provider), new JwtBase64UrlEncoder(), new RS256Algorithm(pubprivCertificate));
- var decodedToken = jwtDecoder.Decode(token.Token);
- Console.Out.WriteLine($"Decoded token {decodedToken}");
- return decodedToken;
+ Console.WriteLine($"Token (expiring: {token.IsExpiring()}): {token.Token}");
+ Console.WriteLine($"Token valid until: {validatedToken.ValidTo}");
}
}
}
\ No newline at end of file
diff --git a/KS.Fiks.Maskinporten.Client/KS.Fiks.Maskinporten.Client.csproj b/KS.Fiks.Maskinporten.Client/KS.Fiks.Maskinporten.Client.csproj
index ccb2ec1..3e373c0 100644
--- a/KS.Fiks.Maskinporten.Client/KS.Fiks.Maskinporten.Client.csproj
+++ b/KS.Fiks.Maskinporten.Client/KS.Fiks.Maskinporten.Client.csproj
@@ -14,7 +14,7 @@
git
FIKS
1.1.6
- net6.0;netcoreapp3.1;netstandard2.0
+ net6.0;netstandard2.0
true
true
snupkg
diff --git a/KS.Fiks.Maskinporten.Client/MaskinportenClientConfiguration.cs b/KS.Fiks.Maskinporten.Client/MaskinportenClientConfiguration.cs
index 55a3ab5..7d49cc4 100644
--- a/KS.Fiks.Maskinporten.Client/MaskinportenClientConfiguration.cs
+++ b/KS.Fiks.Maskinporten.Client/MaskinportenClientConfiguration.cs
@@ -22,6 +22,11 @@ public MaskinportenClientConfiguration(
throw new ArgumentException("Either certificate or private and public key must be set!");
}
+ if (certificate != null && (privateKey != null || publicKey != null))
+ {
+ throw new ArgumentException("Only certificate or public/private key must be set. Not both");
+ }
+
Audience = audience;
TokenEndpoint = tokenEndpoint;
Issuer = issuer;