node.jsazureazure-active-directory

How can I sign a JWT to exchange an access token from azure active directory?


I am coding by nodejs and I am referring to this doc :

https://learn.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate

As this doc said , I can get an access token by a JWT token . This doc indicated how to sign a JWT :

https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials

but I can't find any demo code about it.So how can I implement it to get an access token to call microsoft graph apis by nodejs ?

Any assistance is appreciated, thanks !


Solution

  • To go through this whole process , we should create certs first. I use self-signed certs for demo here .

    Step 1 : Create .cer and .key files, we will upload .cer to Azure AD App and use .key file to sign our JWT tokens.

    1) Create a self signed cert which password is 123456 by Powershell :

    $cert = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname stantest.com
    $pwd = ConvertTo-SecureString -String '123456' -Force -AsPlainText
    $path = 'cert:\localMachine\my\' + $cert.thumbprint 
    Export-PfxCertificate -cert $path -FilePath <path of your pfx file> -Password $pwd
    

    2) Create .cer file based on .pfx file in CMD:

    openssl pkcs12 -in <path of .pfx file> -clcerts -nokeys -out <path of .cer> 
    

    3) Create .key file based on .pfx file in CMD:

    openssl pkcs12 -in <path of .pfx file> -nocerts -nodes  -out <path of .pem file>
    openssl rsa -in <path of .pem file> -out <path of .key file>
    

    Finally , we will get files below : enter image description here

    STEP 2 : Upload .cer file to your Azure AD app and note its Thumbprint value:

    enter image description here

    STEP 3 : Use the nodejs code below to sign a JWT and exchange an access token for Microsoft Graph APIs :

    var jwt = require("jsonwebtoken");
    var fs = require("fs");
    var uuidv1 = require('uuid/v1');
    var fetch = require("node-fetch");
    
    
    var tenant = "<your tenant ID/Name>";
    var clientID = "<your Azure AD app ID>";
    var certThumbprint = "<.cer Thumbprint value on Azure portal>";
    var privateKey = fs.readFileSync("<path of your .key file>").toString();
    
    
    var certOctets = certThumbprint.match(/.{1,2}/g)
    var certBuffer = Buffer.alloc(certOctets.length)
        for(var i=0; i<certOctets.length; i++){
            certBuffer.writeUInt8(parseInt(certOctets[i], 16), i);
        }
    //Perform base64url-encoding as per RFC7515 Appendix C
    var x5t = certBuffer.toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
    
    
    var current = Date.now().toString().substr(0,10);
    
    var payload= 
    {
        "aud":"https://login.microsoftonline.com/"+tenant+"/oauth2/token",
        "exp": Number(current) + 3600,
        "iss":clientID,
        "jti":uuidv1(),
        "nbf":Number(current),
        "sub":clientID
    }
    var token = jwt.sign(payload,privateKey,{algorithm: 'RS256',header: {"x5t": x5t}})
    
    var reqTokenBody = 
    "grant_type=client_credentials&"+
    "client_id="+clientID + "&" +
    "resource=https://graph.microsoft.com&"+ 
    "client_assertion="+ token +"&" + 
    "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    
    
    fetch("https://login.microsoftonline.com/hanxia.onmicrosoft.com/oauth2/token",
        {
        method: 'POST',
        headers: 
            {
            'Content-Type': 'application/x-www-form-urlencoded',
    
            },
        body:reqTokenBody,
        }).then((response) => response.json()).then((data) =>
            {
            console.log(JSON.stringify(data, null, 2));
            }).catch((error) =>
            {
            console.log(error);
            });
    

    Result :

    enter image description here

    Hope it helps.