Performing Additional Checks on JWT Access Tokens

Before you start, make sure that you have read the cookbook chapter Protecting Access using JWT Tokens.

Let's start with the following swagger definition:

swagger.yaml:

swagger: "2.0"
host: my-api.com
schemes:
  - https
basePath: /
securityDefinitions:
  JWTCookie:
    type: apiKey
    in: header
    name: Cookie
    x-flat-cookiename: authtoken
    x-flat-jwt:
      key:
        file: pubkey.pem
      alg: RS256
      out-var: $jwt
security:
  - JWTCookie: []
paths:
  /projects/{p}:
    x-flat-flow: 
    get:
      parameters:
        - name: p
          in: path
          description: The project identifier
          type: string
          required: true
    patch:
      parameters:
        - name: p
          in: path
          description: The project identifier
          type: string
          required: true

The API has one endpoint with a path parameter p indicating the project identifier and two operations (GET and PATCH). The whole API is secured with a security scheme labelled "JWTCookie".

FLAT will make sure that every request to this endpoint

  • has a Cookie header

  • with a value for the authtoken cookie

  • that is a JWT

  • properly signed and

  • not expired.

In addition to this, FLAT provides features for further checks:

Checking Claims

For example, you can ensure that the token was issued by a specific token provider (iss claim)


    x-flat-jwt:
      key:
        file: pubkey.pem
      alg: RS256
      out-var: $jwt
      claims:
        iss: "https://trustworthy-token-provider.com"  # ⬅ the mandatory value for the iss claim

and that your API is (one of) the intended audience(s) for the token (aud claim)


    x-flat-jwt:
      key:
        file: pubkey.pem
      alg: RS256
      out-var: $jwt
      claims:
        iss: "https://trustworthy-token-provider.com"
        aud: "https://my-api.com/"    # ⬅ a mandatory value for the aud claim

A JWT with the following claims will pass the test:

{
  "iss": "https://trustworthy-token-provider.com",
  "aud": [ "https://my-api.com/", "https://a-different-api.org/" ],

}

while

{
  "iss": "https://the-reckless-token-provider.com",
  "aud": [ "https://my-api.com/", "https://a-different-api.org/" ],

}

or

{
  "iss": "https://trustworthy-token-provider.com",
  "aud": [ "https://a-different-api.org/" ],

}

will not pass.

Checking Scopes

Let's restrict the use of the PATCH operation to specially authorized requests. We can use scopes to achieve this:


    patch:
      security:
        - JWTCookie: [ write ]
      parameters:

FLAT will now look for a scope claim (default claim name is scope) with a value of write. If the write scope is present (possibly along with further scopes, like in "scope": "read write create"), the request passes, otherwise it is rejected.

BTW, you can specify another claim name for scopes using the scope-claim property of x-flat-jwt:


    x-flat-jwt:
      key:
        file: pubkey.pem
      alg: RS256
      scope-claim: sc    # ⬅ look for scopes in the sc JWT claim

The post-check flow

Finally, we want to check that a certain non-standard JWT claim pid matches the path param p (the project identifier).

We use the post-check-flow feature:


    x-flat-jwt:
      key:
        file: pubkey.pem
      
      post-check-flow: check-jwt.xml

with check-jwt.xml:

<flow>
  <!-- $jwt contains the JWT claims, see the out-var property or x-flat-jwt -->
  <log>
  {
    "token_id": {{ $jwt/pid }},
    "path_id": {{ $request/params/p }}
  }
  </log>
  <error if="not($jwt/pid) or $jwt/pid != $request/params/p">
  {
    "status": 401,
    "message": "Token is not applicable for this project."
  }
  </error>
</flow>

A JWT with the claim

{

  "pid": "ABC123",

}

will permit access to https://my-api.com/projects/ABC123, but not to https://my-api.com/projects/DEF456.

All files together

swagger.yaml:

swagger: "2.0"
host: my-api.com
schemes:
  - https
basePath: /
securityDefinitions:
  JWTCookie:
    type: apiKey
    in: header
    name: Cookie
    x-flat-cookiename: authtoken
    x-flat-jwt:
      key:
        file: pubkey.pem
      alg: RS256
      claims:
        iss: "https://trustworthy-token-provider.com"
      scope-claim: sc # default: scope
      out-var: $the_claims
      out-header: JWT
      post-check-flow: check-jwt.xml
security:
  - JWTCookie: []
paths:
  /projects/{p}:
    x-flat-flow: ...
    get:
      parameters:
        - name: p
          in: path
          description: The project identifier
          type: string
          required: true
    patch:
      security:
        - JWTCookie: [ write ]
      parameters:
        - name: p
          in: path
          description: The project identifier
          type: string
          required: true

check-jwt.xml:

<flow>
  <!-- $jwt contains the JWT claims, see the out-var property -->
  <log>
  {
    "token_id": {{ $jwt/pid }},
    "path_id": {{ $request/params/p }}
  }
  </log>
  <error if="not($jwt/pid) or $jwt/pid != $request/params/p">
  {
    "status": 401,
    "message": "Token is not applicable for this project."
  }
  </error>
</flow>

See also

Last updated