Skip to content

Commit

Permalink
working BTP access token
Browse files Browse the repository at this point in the history
  • Loading branch information
gregorwolf committed Dec 24, 2023
1 parent 7411361 commit 7896020
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 7 deletions.
4 changes: 2 additions & 2 deletions app/msal-2/authConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ const msalConfig = {
* https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
*/
const loginRequest = {
scopes: ["api://19702d90-5fb8-4d76-a6c9-557f55c771e8/SAP.ReadWrite"],
scopes: ["api://19702d90-5fb8-4d76-a6c9-557f55c771e8/SAPGraph.access"],
};

/**
* Add here the scopes to request when obtaining an access token for MS Graph API. For more information, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
*/
const tokenRequest = {
scopes: ["api://19702d90-5fb8-4d76-a6c9-557f55c771e8/SAP.ReadWrite"],
scopes: ["api://19702d90-5fb8-4d76-a6c9-557f55c771e8/SAPGraph.access"],
forceRefresh: false, // Set this to "true" to skip a cached token and go to the server to get a new token
};
100 changes: 98 additions & 2 deletions srv/AuthClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ class AuthClient {
xsenv.loadEnv();
var services = {};
try {
services = xsenv.getServices({ azuread: { tag: "azure-ad" } });
services = xsenv.getServices({
azuread: { tag: "azure-ad" },
xsuaa: { tag: "xsuaa" },
});
} catch (error) {
console.error(chalk.red("[azure-ad-auth-client] - " + error.message));
console.error(
Expand All @@ -33,22 +36,115 @@ class AuthClient {
this.appId = services.azuread.clientID;
this.appSecret = services.azuread.clientSecret;

this.ApplicationIDuri = services.azuread.ApplicationIDuri;
this.ApplicationIDuri = services.azuread.IdentifierEntityID;

// V2 AAD path for On-behalf-of flow
this.pathOAuth = `/${this.aadTenantId}/oauth2/v2.0/token`;

this._xsuaaACSURLSuffix = "aws-live";
this.xsuaaUrl = services.xsuaa.url;
this.btpTokenEndpoint = `/oauth/token/alias/${services.xsuaa.identityzone}.${this._xsuaaACSURLSuffix}`;

this.xsuaaClientId = services.xsuaa.clientid;
this.xsuaaSecret = services.xsuaa.clientsecret;

// On behalf of token use
this.tokenUseValue = "on_behalf_of";
// JWT-Bearer token grant type
this.grantTypeJwtBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer";
// Token type SAML2
this.tokenTypeSaml = "urn:ietf:params:oauth:token-type:saml2";
// Token type SAML bearer
this.grantTypeSaml = "urn:ietf:params:oauth:grant-type:saml2-bearer";
// AAD hostname
this.aadBasUrl = "https://login.microsoftonline.com";

this.headerUrlEncoded = "application/x-www-form-urlencoded";
}

// Get BTP Access Token to access SAP Cloud Integration by exchanging SAML Assertion to BTP OAuth token issued by xsuaa
// If token is provided, the access token request can be skipped (e.g. when bot oAuth connection is used)

/**
* This method allows to request an OAuth token for SAP BTP access like calling Integration Flows
*
* Requesting such an OAuth token consists of multiple consecutive steps which will be outlined below.
*
* The first step (_getAccessTokenForBtpSamlAssertion) will make use of the on-behalf-of flow again by using current
* Microsoft Teams token and exchanging it to a an application access token issued by the extension application registration.
* This token will contain a custom scope allowing us to request a SAML assertion in the next step. In case of bot usage,
* this step can be skipped as the OAuth connection feature of the Bot service will return this application access token.
*
* Once the application access token including the custom scope is available, it can be used to obtain a SAML assertion
* from the application registration created when configuring the trust between SAP BTP and Azure AD. This SAML assertion
* can further on be used to retriev a valid oAuth token from SAP BTP (_getSamlAssertionForBtpTokenExchange).
*
* The SAML assertion is send to the XSUAA authentication endpoint of the SAP BTP subaccount. Due to the trust, between
* Azure AD and SAP BTP, XSUAA will process the SAML assertion and issue an oAuth token which can now be used to access
* SAP BTP ressources like Integration Flows of SAP Cloud Integration.
*
*/
async getAccessTokenForBtpAccess(req, token) {
try {
// if (!token) token = await this._getAccessTokenForBtpSamlAssertion(req);
const samlAssertionAzureAd =
await this.getSamlAssertionForBtpTokenExchange(token);

const data = qs.stringify({
assertion: samlAssertionAzureAd,
grant_type: this.grantTypeSaml,
});

// This token endpoint is able to process the SAML assertion
const btpTokenEndpoint = this.xsuaaUrl + this.btpTokenEndpoint;

// Request a new OAuth token for SAP Cloud Integration apiaccess using the SAML assertion
// and the respective client id and secret of the process integration runtime instance.
let res = await (async () => {
try {
let resp = await axios.post(btpTokenEndpoint, data, {
auth: {
username: this.xsuaaClientId,
password: this.xsuaaSecret,
},
headers: {
"Content-Type": this.headerUrlEncoded,
},
});
return resp;
} catch (err) {
console.error(err);
}
})();

// The access token can now be extracted from the result
if (
res.data &&
res.headers["content-type"].includes("application/json")
) {
const responseBody = res.data;
let accessToken = " ";
try {
accessToken = responseBody["access_token"].toString();
return accessToken;
} catch (err) {
console.error("No JSON response. Access Token request failed");
}
} else {
console.error("HTTP Response was invalid and cannot be deserialized.");
}
} catch (err) {
console.error(err);

if (
err.error === "invalid_grant" ||
err.error === "interaction_required"
) {
throw new Error(err.error);
}
}
}

// Get SAML Assertion for BTP Access (on behalf of flow )

/**
Expand Down
6 changes: 3 additions & 3 deletions srv/catalog-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ module.exports = async function (srv) {
const token = getAuthToken(req);
const authClient = new AuthClient();
LOG.debug("Token: " + token);
const samlAssertion = await authClient.getSamlAssertionForBtpTokenExchange(
const btpAccessToken = await authClient.getAccessTokenForBtpAccess(
req,
token
);
LOG.debug("SAML Assertion: " + samlAssertion);
// https://login.microsoftonline.com/{{AAD tenant ID}}/oauth2/v2.0/token
LOG.debug("BTP Access Token: " + btpAccessToken);
});

srv.on("readSAPLogonTicket", async (req) => {
Expand Down

0 comments on commit 7896020

Please sign in to comment.