JSON Web Signature (JWS)

·

6 min read

JSON Web Signature (JWS)

This policy is a data structure representing a MACed (HMAC algorithm) or digitally signed message.
It is signed using a shared key or public/private key.
We can choose different algorithms for these.
Example:
- HS256 algo uses shared key.
- RS256 algo uses public/private key.

How it is different from JWT?
- In JWS, Payload can be in any format but in JWT, it is always a JSON object.
- There is a Detach option in JWS, in this payload is ignored, but payload is always attached to JWT.

It has three structures.
- Header
- Payload (optional)
- Signature
These 3 sections are separated by .(dot) operator
encoded formate ASCII format of header and payload.

JWS Policy implementation.

The client sends the request with a secret key and payload (optional). when it reaches the API proxy, the proxy will add Generate JWS Policy, here it accepts the key and generates the token using Algorithm. The generated token is sent back to the client using the Asign message policy by constructing the message.

JWS Verification Flow:
The client sends a request with a secret key, Payload data (optional), and Authorization barrier <JWS> or Send JWS token from other paths. In the API proxy verify the Token sent using the Verify JWS Policy. and send back the successful message to the client if verification is successful by creating the Success response using the assign message policy.

First proxy:
Create no target proxy

next- passthrough- eval next- edit proxy - develop.
We are sending the JWS token back to the client so
Proxy endpoint - postflow - response - add Generate JWS Policy.

Generate JWS policy code should look like below.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GenerateJWS name="Generate-JWS-1">
    <DisplayName>Generate JWS-1</DisplayName>
    <Algorithm>HS256</Algorithm>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <CriticalHeaders/>
    <SecretKey>
        <Value ref="private.secretkey"/>
    </SecretKey>
    <!-- Payload we will pass it from the request-->
    <Payload ref="request.content"/>
    <!-- Generate the token by applying the secret key on payload content by uing algorithm--> 
    <!-- true, we can detach the content(means we are not adding any content while requesting
    false, meand we are attaching the content-->
    <DetachContent>false</DetachContent>
    <!-- can claim the additional headers name from client like user name-->
    <AdditionalHeaders>
        <Claim name="issuedby" ref="request.header.user1"/>
    </AdditionalHeaders>
    <!-- generated token is stored in variable "jwstokenvalue"-->
    <OutputVariable>jwstokenvalue</OutputVariable>
</GenerateJWS>

we need to get the value private.secretkey. assign a value to it using the assign message policy by taking the value from the query parameter in the request.
Proxy endpoint - postflow - response - add assign message policy.

Before flow goes to Generate JWS policy it should pass through the Assing message-1 policy. So drag it to the first

The assign message-1 policy code should look like the one below.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage continueOnError="false" enabled="true" name="Assign-Message-1">
    <DisplayName>Assign Message-1</DisplayName>
    <Properties/>
    <AssignVariable>
        <Name>private.secretkey</Name>
        <Ref>request.queryparam.key1</Ref>
    </AssignVariable>
    <!--afetr this we need to remove this query param from the request-->
    <Remove>
        <QueryParams>
            <QueryParam name="key1"/>
        </QueryParams>
    </Remove>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <!-- type="response". bcz policy is in response flow-->
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

To send the response back to the client we construct a JSON message to display the generated token to the client.
Proxy endpoint - postflow - response - add assignh message-2 policy.

The assign message-2 policy code should look like the one below.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage continueOnError="false" enabled="true" name="Assign-Message-2">
    <DisplayName>Assign Message-2</DisplayName>
    <Properties/>
    <Set>
        <Headers/>
        <QueryParams/>
        <FormParams/>
        <Verb>POST</Verb>
        <Payload contentType="application/json">
            {
                "JWS_TOKEN": "{jwstokenvalue}"
            }
        </Payload>
        <Path/>
    </Set>

    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <!-- type="response". bcz policy is in response flow-->
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

Save and deploy
see the output: by sending a POST request with key1=code4545 in param, with user1 = and in the header and with body content.

Let’s decode the encoded token.
jwt.io

send the request without payload. before that modify the Generate JWS policy code.
make detach content as true.
the Generate JWS policy code looks like below.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GenerateJWS name="Generate-JWS-1">
    <DisplayName>Generate JWS-1</DisplayName>
    <Algorithm>HS256</Algorithm>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <CriticalHeaders/>
    <SecretKey>
        <Value ref="private.secretkey"/>
    </SecretKey>
    <!-- Payload we will pass it from the request-->
    <Payload ref="request.content"/>
    <!-- true, we can detach the content(means we are not adding any content while requesting
    false, meand we are attaching the content-->
    <DetachContent>true</DetachContent>
    <!-- can claim the additional headers name from client like user name-->
    <AdditionalHeaders>
        <Claim name="issuedby" ref="request.header.user1"/>
    </AdditionalHeaders>
    <!-- generated token is stored in variable "jwstokenvalue"-->
    <OutputVariable>jwstokenvalue</OutputVariable>
</GenerateJWS>

Save and deploy.

Let’s decode the generated token.
jwt.io

Again make detach content as false. save and deploy and see the output.


One more API proxy to verify the JWS.
create no target proxy

Add a policy to verify the token.
proxy endpoint - preflow - request - add Verify JWS policy

Verify JWS policy code should look like below. and save it.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyJWS name="Verify-JWS-1">
    <DisplayName>Verify JWS-1</DisplayName>
    <!--we should use the same algorithm and same secret key as we used to generate th JWS token-->
    <Algorithm>HS256</Algorithm>
    <SecretKey>
        <Value ref="private.secretkey"/>
    </SecretKey>
    <!-- source: we are gettign JWS token from request- form parameter-->
    <Source>request.formparam.JWS</Source>
    <!-- we will use detached content in Generate JWS part so we no need to ude that
    <DetachedContent>false</DetachedContent>
    -->
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <KnownHeaders/>
    <IgnoreCriticalHeaders/>
</VerifyJWS>

we need to get the private.secretkey from the client request through the query parameter
proxy endpoint - preflow - request - add the assign message-1 policy.

This policy should be executed before the Verify JWS policy, so drage it to first.

The assign message-1 policy code should look like the one below. save

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage continueOnError="false" enabled="true" name="Assign-Message-1">
    <DisplayName>Assign Message-1</DisplayName>
    <Properties/>
    <AssignVariable>
        <Name>private.secretkey</Name>
        <Ref>request.queryparam.key1</Ref>
    </AssignVariable>
    <!--afetr this we need to remove this query param from the request-->
    <Remove>
        <QueryParams>
            <QueryParam name="key1"/>
        </QueryParams>
    </Remove>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

Send a success response to the client back, by instructing JSON code, using assign message policy.
proxy endpoint - postflow - response - add assign message-2 policy.

we need to add one condition before executing assign message policy-2 in postflow response.
so update the proxy endpoint(default) code.
proxy endpoint(default) code looks like below. SAVE

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Name>Assign-Message-1</Name>
            </Step>
            <Step>
                <Name>Verify-JWS-1</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>
    <Flows/>
    <PostFlow name="PostFlow">
        <Request/>
        <Response>
            <!--JWS.failed='' (failed = 'blank' means verification successfull. if it is true means verification unsuccessfully)-->
            <Step>
                <Condition>JWS.failed=''</Condition>
                <Name>Assign-Message-2</Name>
            </Step>
        </Response>
    </PostFlow>
    <HTTPProxyConnection>
        <BasePath>/jwsverify</BasePath>
    </HTTPProxyConnection>
    <RouteRule name="noroute"/>
</ProxyEndpoint>

Assign message policy-2 code looks like below.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage continueOnError="false" enabled="true" name="Assign-Message-2">
    <DisplayName>Assign Message-2</DisplayName>
    <Properties/>
    <Set>
        <Headers/>
        <QueryParams/>
        <FormParams/>
        <Verb>POST</Verb>
        <Payload contentType="application/json">
            {
                "Status": "verified successfully"
            }
        </Payload>
        <Path/>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <!-- type="response". bcz policy is in response flow-->
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

save and deploy
output
send a request with a secret key(key1=code4545), the token generated in the first proxy will pass it from the form parameter of the body.