Maskinporten token
Hvordan konstruere JWT assertion og requesten for å hente et Maskinporten token.
Denne leksjonen vil vise hvordan man lager en JWT (JSON Web Token) grant og bruker det til å hente et Maskinporten token. For overordnet beskrivelse refererer vi til Maskinportens guide for API-konsumenter. Koden under er skrevet i C# og .Net Core 3.1 og kan ses i sin helhet på Github.
JWT grant oppsett
JWT grantet består av to deler:
- Header - som beskriver hvordan hvordan JSON Web Tokenet er signert. Dette forutsetter at koden har tilgang til virksomhetssertifkatet.
- Body - som beskriver hvilke
claims
klienten har. De forskjelligeclaims
som må spesifiseres mot Maskinporten er også beskrevet her.
I C#/.Net prosjektet er JWT grantet konstruert på følgende måte:
private string GetJwtAssertion(X509Certificate2 certificate)
{
X509SecurityKey securityKey = new X509SecurityKey(certificate);
List<string> certificateChain = new List<string>()
{
Convert.ToBase64String(certificate.GetRawCertData())
};
// The header of the JWT grant towards Maskinporten must have the
// following claims:
// "alg": "RS256"
// "x5c": "<X.509 Certificate chain>"
JwtHeader header = new JwtHeader(
new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256))
{
{ "x5c", certificateChain }
};
// Remove claims that are not needed for Enterprise Certificate grants.
header.Remove("typ");
header.Remove("kid");
// 120 seconds is the maximum expiration time allowed by Maskinporten.
// This value is chosen to make debugging more lenient.
long issuedAt = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
long expirationTime = issuedAt + 120;
JwtPayload body = new JwtPayload
{
{ "aud", "https://ver2.maskinporten.no/" },
{ "resource", "https://tt02.altinn.no" },
{ "scope", "altinn:enduser" },
{ "iss", _clientId },
{ "exp", expirationTime },
{ "iat", issuedAt },
{ "jti", Guid.NewGuid().ToString() },
};
JwtSecurityToken securityToken = new JwtSecurityToken(header, body);
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.WriteToken(securityToken);
}
X.509 Sertifikat
Hvordan man henter og bruker virksomhetsertifikatet i koden er utelatt fra denne veiledning, men et eksempel kan ses i GetCertificateFromX509Store().
Hente Maskinporten token
Selve HTTP requesten ser slik ut:
POST /token HTTP/1.1
Host: https://ver2.maskinporten.no
Content-type: application/x-form-urlencoded
{
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": "<JWT>"
}
Her skal <JWT>
erstattes av det enkodede JWT grantet vi lagde.
I det tilhørende .Net Core prosjektet har vi benyttet Flurl for å gjøre REST kallene.
public async Task<TokenResponse> PostMaskinportenToken()
{
try
{
X509Certificate2 certificate = GetCertificateFromX509Store();
string jwtAssertion = GetJwtAssertion(certificate);
string response = await "https://ver2.maskinporten.no/"
.AppendPathSegment("token")
.PostUrlEncodedAsync(new
{
grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion = jwtAssertion
})
.ReceiveString();
return JsonSerializer.Deserialize<TokenResponse>(response);
}
catch (FlurlHttpException ex)
{
var status = ex.Call.Response.StatusCode;
var message = await ex.GetResponseStringAsync();
...
Responsen på et vellykket kall vil se slik ut:
"{\"access_token\":\"eyJraWQiOiJjWmswME1rbTVIQzRnN3Z0NmNwUDVGSFpMS0pzdzhmQkFJd
UZiUzRSVEQ0IiwiYWxnIjoiUlMyNTYifQ.eyJhdWQiOiJodHRwczpcL1wvdHQwMi5hbHRpbm4ubm8i
LCJzY29wZSI6ImFsdGlubjplbmR1c2VyIiwiaXNzIjoiaHR0cHM6XC9cL3ZlcjIubWFza2lucG9ydG
VuLm5vXC8iLCJjbGllbnRfYW1yIjoidmlya3NvbWhldHNzZXJ0aWZpa2F0IiwidG9rZW5fdHlwZSI6
IkJlYXJlciIsImV4cCI6MTY0Mjc1NTExNSwiaWF0IjoxNjQyNzU0OTk1LCJjbGllbnRfaWQiOiJkMz
k1MDQzZi0xOWRlLTQ4ZjctOWVhNy05ZTM4Yzk2YjU0NzIiLCJqdGkiOiJIZWZhM0ljeFp4aXZfazdq
NkhEY1VXSnhrTUIzVGlkQ0RWQlFyZ0dGWGdVIiwiY29uc3VtZXIiOnsiYXV0aG9yaXR5IjoiaXNvNj
UyMy1hY3RvcmlkLXVwaXMiLCJJRCI6IjAxOTI6OTkxODI1ODI3In19.fCH89kCuOmdOTpkhxB6l57U
Ll7B-iXvbgpJdZmE6NqrXs5Vu8J-zPC8evhVRwQ8H7J13wJQiUwmP-3IRCtuUFurLPU9Mk2_wIrkzC
u0gPigJ4nFbDZOiOSoagAHt9jSEF8fFQkPif0MgcE2mqYkYRS2QqjQqORLWRhDAxCltxFphhFFuzxH
D7ogGaKQnJejnlWcUq6ZnEz-GeyupRSAy-Z8qZeXohiAbiNsDFXcOulJI29ivUduuGnLE07zyNXLu6
cb8fGtyTU_C2lIrR3Q0mRYxAVk-Gb6Np_uQ5UZVUjPiJPBb6bwS9uWXjrlbDsPdKnsLcysBPuOfZMn
aMrDO_g\",\"token_type\" :\"Bearer\",\"expires_in\":119,\"scope\":\"altinn:end
user\"}"
Man kan inspisere access_token
verdien ved å dekode ved hjelp av jwt.io.
For å deserialisere strengen som ble returnert til et objekt kan man bruke biblioteket System.Text.Json
.
Objektet eller modellen for responsen ser slik ut:
using System.Text.Json.Serialization;
// The TokenResponse from Maskinporten
public class TokenResponse
{
// An Oauth2 access token, either by reference or as a JWT depending
// on which scopes was requested and/or client registration properties.
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
[JsonPropertyName("expires_in")]
public int ExpiresIn { get; set; }
// The list of scopes issued in the access token. Included for
// convenience only, and should not be trusted for access control
// decisions.
[JsonPropertyName("scope")]
public string Scope { get; set; }
// Type of token
[JsonPropertyName("token_type")]
public string TokenType { get; set; }
}
Det er strengen fra access_token
som er skal brukes som bearer token
i neste leksjon for å hente Altinn autentiseringstoken.