'Does my web frontend JWT authentication process prevent security breaches? [closed]

I have an authentication procedure and I'm wondering if I'm not mixing up some concepts here. Basically:

Login:

  1. returns a signed JWT stored in memory with an hashed fingerprint as claim
  2. store httpOnly and Secure refresh token (JWT) on client with longer expiration time
  3. store fingerprint in secure httpOnly cookie

Authentication:

  1. Send a bearer access token via header
  2. Send XSRF token via header
  3. Verify retrieved XSRF token is valid in server
  4. retrieve fingerprint in server
  5. check in DB if refreshToken is valid
  6. verify access token validity and compare retrieved hashed fingerprint value with JWT fingerprint claim

Access token expired:

  1. check for CSRF token validity
  2. request a new token on refresh token route
  3. Check Refresh Token Validity
  4. Send new signed JWT access token with fingerprint

Does it sound enough for preventing both XSS and CSRF attacks (removing harmful html tag apart for XSS)?



Solution 1:[1]

Important question! JWT is widely used, but since it provides a lot flexibility, it's often configured and used in a way that opens security vulnerabilities.

Based on recommendations seen here, your cookie with the user fingerprint could also:

  • use SameSite=strict so that the cookie is sent only ever to the same site as the one that originated it (helps to avoid CSRF)
  • has the __Host- prefix and meets the associated requirements, as explained here (further helps to avoid session hijacking)

As for access and refresh token storage on the web frontend, yes, storing in memory is safer according to the same source (Window.sessionStorage is suggested there). EDIT: see also links in the comments.

In addition to signing the token, depending on the scenario, the token payload could also be encrypted with AES and use nonce.

Anyone reading this who feels JWT is still not secure, I'd suggest taking a look at PASETO.

Solution 2:[2]

Probably because the IdP was not enabled using the issuers collection.

In all your Google IdP technical profiles, make sure to have:

  <OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="google.com" AlwaysUseDefaultValue="true" />

In Google-OAUTH-Link technical profile, make sure to have this:

<Metadata>
  <Item Key="ClaimTypeOnWhichToEnable">currentIssuers</Item>
  <Item Key="ClaimValueOnWhichToEnable">google.com</Item>
</Metadata> 
<!--snip-->
<OutputClaimsTransformations>
  <OutputClaimsTransformation ReferenceId="CreateUserIdentityToLink" />
  <OutputClaimsTransformation ReferenceId="AppendUserIdentityToLink" />
</OutputClaimsTransformations>
<EnabledForUserJourneys>OnItemExistenceInStringCollectionClaim</EnabledForUserJourneys>

In the HandleLinkLocalToSocial subjourney, make sure to add Google IdP in the linking steps:

        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.idpselections">
<!--snip preconditions-->
          <ClaimsProviderSelections DisplayOption="ShowSingleProvider">
            <ClaimsProviderSelection TargetClaimsExchangeId="LinkGoogleExchange1"/>
          </ClaimsProviderSelections>
        </OrchestrationStep>

        <OrchestrationStep Order="2" Type="ClaimsExchange">
<!--snip preconditions-->
          <ClaimsExchanges>
            <ClaimsExchange Id="LinkGoogleExchange1" TechnicalProfileReferenceId="Google-OAUTH-Link"/>
          </ClaimsExchanges>
        </OrchestrationStep>

        <OrchestrationStep Order="3" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<!--snip preconditions-->
          <ClaimsProviderSelections DisplayOption="ShowSingleProvider">
            <ClaimsProviderSelection TargetClaimsExchangeId="LinkGoogleExchange2"/>
        </OrchestrationStep>

        <OrchestrationStep Order="4" Type="ClaimsExchange">
<!--snip preconditions-->
          <ClaimsExchanges>
            <ClaimsExchange Id="LinkGoogleExchange2" TechnicalProfileReferenceId="Google-OAUTH-Link"/>
          </ClaimsExchanges>
        </OrchestrationStep>  

Solution 3:[3]

Had the same error using the auto-linking sample code. Check the output claims section of the technical profile AAD-FindLocalAccountWithSocialEmail. The claim currentUserIdentities is read from the directory there and is the input for the ExtractCurrentIssuers output claims transformation of this TP (which populates the currentIssuers claim and puts it into the claims bag). The problem seems to be a wrong PartnerClaimType value.

Try

<OutputClaim ClaimTypeReferenceId="currentUserIdentities" PartnerClaimType="identities"/>

instead of

<OutputClaim ClaimTypeReferenceId="currentUserIdentities" PartnerClaimType="userIdentities"/>

If the PartnerClaimType is wrong the result is empty and an empty currentIssuers string collection leads to the error (in Google-OAUTH-Link TP) you described. Would be nice if App-Insights could show the values of claims collections.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 assitopia