<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Greg Simons</title>
    <description>The latest articles on Forem by Greg Simons (@gregsimons).</description>
    <link>https://forem.com/gregsimons</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F289682%2F84fc4d25-3afb-48c4-8c4c-2612d8af5c4c.jpeg</url>
      <title>Forem: Greg Simons</title>
      <link>https://forem.com/gregsimons</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gregsimons"/>
    <language>en</language>
    <item>
      <title>Spring Security private_key_jwt with AWS KMS</title>
      <dc:creator>Greg Simons</dc:creator>
      <pubDate>Mon, 16 Jan 2023 14:44:01 +0000</pubDate>
      <link>https://forem.com/gregsimons/spring-security-privatekeyjwt-with-aws-kms-3cm4</link>
      <guid>https://forem.com/gregsimons/spring-security-privatekeyjwt-with-aws-kms-3cm4</guid>
      <description>&lt;p&gt;Spring security has long had great OAuth2.0 support from both the server and client elements. Recently spring security added support for the private_key_jwt client authentication method as part of the authorization code grant flow. &lt;br&gt;
&lt;a href="https://github.com/spring-projects/spring-security/pull/9933" rel="noopener noreferrer"&gt;Spring Security GitHub ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The configuration for the key signing requires both the public and private key to be available to the application. Key Management Service (KMS) by Amazon Web Services (AWS) provides services to centrally manage your keys for encryption and signing and is an option to allow a more centralised mechanism for key rotation and policy management. &lt;/p&gt;

&lt;p&gt;In order to add this support in to Spring it requires a number of customisations to be made which will be explained in this post. If your AuthZ server also supports elliptic-curve digital signature algorithms (ECDSA) for the ID token I will outline the additional Bean configuration required as Spring security defaults to RS256 supported with RSA keys.&lt;/p&gt;
&lt;h2&gt;
  
  
  private_key_jwt
&lt;/h2&gt;

&lt;p&gt;The private key jwt client authentication method requires a jwt token to be sent alongside client id, code as a client assertion to the token endpoint. This jwt will need to be signed and then sent in a client_assertion parameter to the auth server. A number of algorithms are supported by various auth servers and can be found by visiting the configuration endpoint of the auth server. &lt;/p&gt;

&lt;p&gt;Full details of the spec can be found here &lt;a href="https://www.rfc-editor.org/rfc/rfc7523.html" rel="noopener noreferrer"&gt;rfc 7523&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first configuration change required is to switch out the default access token response client as part of the HttpSecurity config. This provides the ability to customise the token endpoint request parameters to enrich with the client assertion signed by kms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Security&lt;/span&gt; &lt;span class="nc"&gt;FilterChain&lt;/span&gt; &lt;span class="nf"&gt;filterchain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;....&lt;/span&gt;
  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth2Login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oauth2Login&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;oauthLogin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tokenEndpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenEndpoint&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;tokenEndpoint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accessTokenResponseClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessTokenResponseClient&lt;/span&gt;&lt;span class="o"&gt;))).&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth2Client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizationCodeGrant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authorizationCodeGrant&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;authorizationCodeGrant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accessTokenResponseClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessTokenResponseClient&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;

  &lt;span class="o"&gt;....&lt;/span&gt;
  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OAuth2AccessTokenResponseClient
&lt;/h2&gt;

&lt;p&gt;In order to override the request entity handling to add support for the kms signing you need to add a custom request entity converter to the access token response client that you create.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;OAuth2AccessTokenResponseClient&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OAuthAuthorizationCodeGrantRequest&lt;/span&gt; &lt;span class="nf"&gt;accessTokenResponseClient&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;....&lt;/span&gt;
  &lt;span class="nc"&gt;DefaultAuthorizationCodeTokenResponseClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultAuthorizationCodeTokenResponseClient&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequestEntityConverter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this point it is now necessary to create the converter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is important to note whether or not you are creating the beans or are they being managed and created by Spring as you could incur a number of errors if the objects are manually instantiated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The converter is the main class that allows the overriding of the request parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomKMSJWTClientAuthNConverter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Converter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OAuth2AuthorizationCodeGrantRequest&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RequestEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;OAuth2AuthorizationCodeGrantRequestEntityConverter&lt;/span&gt; &lt;span class="n"&gt;defaultConverter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CustomKMSJWTClientAuthNConverter&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;defaultConverter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OAuth2AuthorizationCodeGrantRequestEntityConverter&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RequestEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OAuth2AuthorizationCodeGrantRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;signedJWT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createSignedJwt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;RequestEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;MultiValueMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MultiValueMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBody&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client_assertion_type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client_assertion"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signedJWT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RequestEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHeaders&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethod&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUrl&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a signed JWT
&lt;/h2&gt;

&lt;p&gt;The next step is to create the JWT and use the KMS API to sign the token.&lt;/p&gt;

&lt;p&gt;You will need to setup the AWS SDK and KMS Client. For example, I exported the AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN and AWS_ACCESS_KEY_ID environment variables and set them on the command line. There are a number of alternatives supported in spring that can be found here: &lt;a href="https://aws.amazon.com/blogs/opensource/getting-started-with-spring-boot-on-aws-part-1/" rel="noopener noreferrer"&gt;AWS SDK on Spring&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="n"&gt;kmsClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;KmsClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"....."&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;credentialsProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DefaultCredentialsProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
  &lt;span class="c1"&gt;// tbc values can be configured with your auth server&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;createSignedJwt&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;JWTClaimSet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;claimSetBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JWTClaimSet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;tbc&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;tbc&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;tbc&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;expirationTime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tbc&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;issueTime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;())).&lt;/span&gt;&lt;span class="na"&gt;jwtId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;tbc&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;JWTClaimSet&lt;/span&gt; &lt;span class="n"&gt;claimSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;claimSetBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claimSet&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have a JWT token ready to sign and verify with KMS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JWTClaimSet&lt;/span&gt; &lt;span class="n"&gt;claimSet&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="o"&gt;....&lt;/span&gt;
      &lt;span class="c1"&gt;// choose a token alg based on what is supported by your auth Server&lt;/span&gt;
      &lt;span class="nc"&gt;JWSHeader&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JWSHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TOKEN_ALG&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

      &lt;span class="nc"&gt;Base64URL&lt;/span&gt; &lt;span class="n"&gt;encodedHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toBase64URL&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
      &lt;span class="nc"&gt;Base64URL&lt;/span&gt; &lt;span class="n"&gt;encodedClaims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Base64URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claimSet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
      &lt;span class="c1"&gt;// create String to sign with KMS&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;signingString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encodedHeader&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;encodedClaims&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
      &lt;span class="o"&gt;....&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now need to use the AWS SDK to create the signing request to pass to KMS to get the signed JWT.&lt;/p&gt;

&lt;p&gt;Key Id is the id of your key from AWS.&lt;br&gt;
Signing alg can be selected from a predefined set using&lt;br&gt;
software.amazon.awssdk.services.kms.model.SigningAlgorithmSpec;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="o"&gt;....&lt;/span&gt;
  &lt;span class="nc"&gt;SignRequest&lt;/span&gt; &lt;span class="n"&gt;signRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SignRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SDKBytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromByteArray&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signingString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keyId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;KEY_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signingAlgorithm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SIGNING_ALG&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

  &lt;span class="nc"&gt;SignResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kmsClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signRequest&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;asByteArray&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above call now provides a Base64 encoded version of the signed string that is attached to the request parameters for the invocation of the token endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  ID Token Verification
&lt;/h2&gt;

&lt;p&gt;Depending on the supported algorithms for the id token you might find that the ID Token is signed with a different algorithm than the default RS256 that spring supports. In order to override this setting you can again create a custom Bean that sets alternative signatures using the JWSAlgorithmResolver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;JwtDecoderFactory&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClientRegistration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;idTokenDecoderFactory&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;OidcTokenDecoderFactory&lt;/span&gt; &lt;span class="n"&gt;idTokenDecoderFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OidcTokenDecoderFactory&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;idTokenDecoderFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJwsAlgorithmResolver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clientRegistration&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;SignatureAlgorithm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RS512&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;idTokenDecoderFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>typescript</category>
    </item>
    <item>
      <title>QLDB, is it the perfect DDD aggregate store ?</title>
      <dc:creator>Greg Simons</dc:creator>
      <pubDate>Tue, 23 Mar 2021 22:23:59 +0000</pubDate>
      <link>https://forem.com/gregsimons/qldb-is-it-the-perfect-ddd-aggregate-store-3njh</link>
      <guid>https://forem.com/gregsimons/qldb-is-it-the-perfect-ddd-aggregate-store-3njh</guid>
      <description>&lt;p&gt;There has been much talk about the ideal aggregate store in domain driven design (DDD) over the years. One particular post that drew my attention back then was this one from Vaughn Vernon &lt;a href="https://kalele.io/the-ideal-domain-driven-design-aggregate-store/"&gt;https://kalele.io/the-ideal-domain-driven-design-aggregate-store/&lt;/a&gt; that reached the consensus that some form of serialized json in a document store was optimal. In this approach fields themselves can still remain queryable.&lt;/p&gt;

&lt;p&gt;This approach had the added benefit of limiting the complicated mapping that inevitably ensued from object-relational-mapping (ORM) tools. For anyone who has tried DDD without event sourcing and hit some of the encapsulation issues, you will know what I mean. &lt;/p&gt;

&lt;h2&gt;
  
  
  Change data capture (CDC) (transactional outbox)
&lt;/h2&gt;

&lt;p&gt;One additional highlight of that article is the reference to domain events and persisting these within an ACID transaction with the aggregate.&lt;/p&gt;

&lt;p&gt;A pattern that has certainly grown a lot more popularity recently is the CDC outbox pattern which prescribes an outbox table where data can be streamed out to analytics and big data products such as &lt;a href="https://pinot.apache.org/"&gt;Apache Pinot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kbastani/pinot-debezium-basic-example"&gt;CDC Outbox pattern with MySQL, Debezium &amp;amp; Apache Pinot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For further information on this pattern there are detailed explanations on the concept in the following posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://microservices.io/patterns/data/transactional-outbox.html"&gt;https://microservices.io/patterns/data/transactional-outbox.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/"&gt;https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Event Sourcing
&lt;/h2&gt;

&lt;p&gt;A number of previous organisations I have been part of have been led down the Event Sourcing (ES) path due to the promises of intrinsic audit and the ability to recreate views on records to previous states. The reality of these approaches can often be fraught with danger without significant experience. In my experience, it has often resulted in applications that were difficult to design, adapt and manage.&lt;/p&gt;

&lt;p&gt;This is where a database technology such as Quantum Ledger Database from Amazon Web Services (AWS) can really help simplify the engineering process and help deliver similar benefits to the event sourcing approach but without the added complexity that it can bring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quantum Ledger Database (QLDB)
&lt;/h2&gt;

&lt;p&gt;QLDB is a fully managed ledger database that provides a transparent, immutable, and cryptographically verifiable transaction log owned by a central trusted authority. Amazon QLDB tracks each and every application data change and maintains a complete and verifiable history of changes over time. &lt;a href="https://qldbguide.com/"&gt;QLDB Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qWDMvyFz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqcz5h9ox0blkngn1bti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qWDMvyFz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqcz5h9ox0blkngn1bti.png" alt="QLDB Overview" width="880" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/m_lewis"&gt;Matthew Lewis&lt;/a&gt; has written a very useful introduction around the key concepts behind this new style of database and for that reason I will not repeat that detail here. &lt;a href="https://teachmyselfcloud.com/post/intro-to-qldb.html"&gt;Intro to QLDB&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the purposes of the reference to the perfect aggregate store, QLDB supports the following key attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create cryptographically verifiable versions of your document&lt;/li&gt;
&lt;li&gt;documents can be stored with an intention on querying the materialized view of the events within journal&lt;/li&gt;
&lt;li&gt;while it doesn't allow you to stream from an outbox table, you can stream from the ledger and filter/reconstruct an event message from something like AWS Lambda&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  QLDB and the CDC Outbox pattern
&lt;/h3&gt;

&lt;p&gt;QLDB provides QLDB Streams to support attaching a kinesis data stream that allows you to stream data from the journal into kinesis from any point in time. This then enables functionality such as additional views in search databases such as Elastic Search or analytics tooling such as Apache Pinot. QLDB streams takes care of all the configuration around partition keys for kinesis. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kq57iAWL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2pjx6g4jbb5rewvebg7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kq57iAWL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2pjx6g4jbb5rewvebg7.png" alt="QLDB &amp;amp; Pinot" width="571" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The figure above illustrates the mechanism by which data can be transmitted to Online Analytical Processing (OLAP) tooling. I will be producing some demos to back up this post showing CDC Outbox in to Apache Pinot using the new Kinesis connector which was unveiled in a &lt;a href="https://twitter.com/nehapawar18/status/1347256958802227201"&gt;sneak preview&lt;/a&gt; by &lt;a href="https://twitter.com/nehapawar18"&gt;Neha Pawar&lt;/a&gt; in Jan 21.&lt;/p&gt;

&lt;p&gt;For those of you that want to take a look at the CDC Outbox style pattern with QLDB and Kinesis, Matthew Lewis has created a &lt;a href="https://github.com/AWS-South-Wales-User-Group/qldb-simple-demo"&gt;demo&lt;/a&gt; available on github which highlights this process. It is also available as a running reference example that you can play around with at &lt;a href="https://demo.qldbguide.com/"&gt;demo.qldbguide.com&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  So is QLDB the perfect aggregate store?
&lt;/h1&gt;

&lt;p&gt;Possibly, time will tell as the product evolves and it gains more traction. It does however provide some critical features not available in other database technology that provides greater verifiable integrity of your records. &lt;/p&gt;

&lt;p&gt;Thanks for taking the time to read!&lt;/p&gt;

</description>
      <category>qldb</category>
      <category>ledger</category>
      <category>pinot</category>
      <category>olap</category>
    </item>
  </channel>
</rss>
