From 1a5931a3433b350c3fb7c1fcc2de127b96982b55 Mon Sep 17 00:00:00 2001 From: Andreas Lilleskare <7822644+exoen@users.noreply.github.com> Date: Wed, 31 May 2023 15:46:21 +0200 Subject: [PATCH] Add example (#130) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add example * Funker for Cert sjekk også --------- Co-authored-by: Jarle Børsheim --- ExampleApplication/ExampleApplication.csproj | 6 +- ExampleApplication/Program.cs | 94 ++++++++++++++----- .../KS.Fiks.Maskinporten.Client.csproj | 2 +- .../MaskinportenClientConfiguration.cs | 5 + 4 files changed, 80 insertions(+), 27 deletions(-) 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;