While I recommend in most cases using Azure AD as IDP, one of the most interesting questions have been: ”how to authorize non-Azure-AD JWT tokens for App service?” – Luckily for us Microsoft released a feature just for this a while back, a possibility to reference custom authentication provider for App Service – and this feature supports authorizing your own tokens.
Backround
If you want to know more about the background, I recommend reading the following MS article:
Configuration notes
- Issuer must match that of defined in the metadata file
- clientID must be included in audience value
- The token has to be signed with the private key of the IDP (or client in this case)
In the picture above the client can create token with self defined claims, as long as the issuer and signature value match to that of the authentication conf
Deployment
- deploy your own public key as JWKS metadata to any suitable service (I used functions for this)
- I used GitHub actions to create the JWT provider and RSA key pair, then publish them with the function
- Private key is produced as artifact for testing
- The solution is deployed into my honeypot 🙂
- Ensure that you include X5C key type
2. In App Service Authentication reference the metadata
3. Once the deployment is done, I used the private key to create signed token, and then send it in authorization header to app service, which was configured with the authentication settings provided in metadata
References (code snippets)
- Github actions for deployment
- Node js with JSONwebtoken and axios libraries
GitHub deployment file
name: Node CI
on: [push]
permissions:
id-token: write
contents: read
jobs:
create-JWKS:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: npm install, build, and test
run: |
npm install
npm install pem-jwk --global
- name: generate metadata
run: |
node main.js
- name: Archive jwks file
uses: actions/upload-artifact@v2
with:
name: jwks
path: |
jwks
- name: Create archive for the signin key
uses: actions/upload-artifact@v2
with:
name: privateKey
path: |
private1.pem
create-Server:
runs-on: ubuntu-latest
needs: create-JWKS
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Download JWKS file
uses: actions/download-artifact@v2
with:
name: jwks
- name: Setup JWKS server
run: |
cp jwks ./metadataserver/azapp/public/.well-known/
cat metadataserver/azapp/public/.well-known/jwks
cd metadataserver
cd azapp
npm install
ls;ls
- name: 7Z
run: |
cd metadataserver
7z a -tzip deploy.zip . -r -mx0 -xr\!*.git -xr\!*.vscode
mv deploy.zip $GITHUB_WORKSPACE
- name: Create archive for the Azure Functions Metadata server
uses: actions/upload-artifact@v2
with:
name: deploy.zip
path: |
deploy.zip
AZ-CLI-WORK:
runs-on: ubuntu-latest
needs: create-Server
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: 'Az CLI login'
uses: azure/login@v1
with:
client-id: 50da2850-3ad2-409c-aafb-7bd76961f75a
tenant-id: 033794f5-7c9d-4e98-923d-7b49114b7ac3
subscription-id: 3539c2a2-cd25-48c6-b295-14e59334ef1c
- name: Download JWKS file
uses: actions/download-artifact@v2
with:
name: deploy.zip
- name: Azure CLI script
uses: azure/CLI@v1
with:
inlineScript: |
chmod +x $GITHUB_WORKSPACE/sampleScript.sh
$GITHUB_WORKSPACE/sampleScript.sh
Client-side Node code for issuing tokens with the Private key Artifact
const {createToken} = require('./src/getAADtokenWithCert')
const {getKey, jwtverify} = require('./src/helpers')
const { decode } = require('jsonwebtoken')
const { default: axios } = require('axios')
// Destructure config for reading Public and Private Key's
//
var priv = require('fs').readFileSync('./private1.pem').toString()
//Wrap into async function
async function getToken() {
var claims = {
"aud": `1e73570f-5af0-4157-bec5-71783dfe6e64`,
"iss": "https://fn-honeyPot-wellknown.azurewebsites.net",
"sub": "1e73570f-5af0-4157-bec5-71783dfe6e64",
"actor":"jose@securecloud.blog"
}
var det =await getKey(`${claims.iss}/.well-known/jwks`)
var {x5t, key} = det
var jwt = await createToken(x5t,priv,claims).catch((error) => {
return error
}
)
var res =await jwtverify(jwt,key)
console.log(res)
console.log('')
var opt = {
url:"https://consumerappforjwt.azurewebsites.net/",
method:"get",
headers:{
authorization: `Bearer ${jwt}`
}
}
console.log('s')
var data= await axios(opt
).catch((error) =>{
console.log(error?.response)
})
console.log(data?.data)
return jwt
}
getToken().then((token) => {
console.log(token)
console.log('Success:',decode(token,{complete:true}))
})
End
These are just snippets to give idea of the deployment workflow and usage. If there is interest I can create a public repo with the code.
0 comments on “Azure App Service – Authorize custom JWT tokens from API clients”