[Python] MSAL python OBO authorization flow gives error AADSTS90014: The required field 'iss'...

Discussão em 'Python' iniciado por Stack, Agosto 29, 2025.

  1. Stack

    Stack Membro Participativo

    I have a SPA front-end and python function app back-end. I had originally been using managed functions via Azure Static Web Apps, but I was running into a lot of errors and ended up switching to a bring-your-own-functions setup. The front-end uses msal-browser'- PublicClientApplication, creating an auth token and calling the backend API with it. The API then uses msal-python's ConfidentialClientApplication to do an OBO auth flow. however, msal_app.acquire_token_on_behalf_of returns with the error in the title: AADSTS90014: The required field 'iss' is missing from the credential. I did have to generate a new certificate and key since the last time this authentication path was working (which i only know of to be when it was still managed, as i had a lot of issues getting a function app to deploy lol), and this new certificate is in the Entra ID App Registration. It does not seem to be giving me an error when creating the ConfidentialClientApplication as if the credentials are malformed.

    MRE:

    On Azure, I have an app registration in Microsoft Entra ID. To that, I uploaded server.crt, obtained by following the instructions from this Microsoft page.

    The other parts (thumbprint, private key) are set as environment variables on the function app (which I am aware is not the best setup but key vault costs money every time you deploy so i will set it up only once i have a functioning application)

    here is the code that I have actually tested and does deploy and function, but still gives the same error otherwise: https://github.com/cedro-gasque/stackoverflow-mre/

    import json
    from msal import ConfidentialClientApplication
    import os
    import azure.functions as func

    app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

    authority = os.getenv("APPSETTING_MSAL_AUTHORITY")
    client_id = os.getenv("APPSETTING_MSAL_CLIENT_ID")

    msal_credential = {
    "private_key": os.getenv("APPSETTING_MSAL_PRIVATE_KEY"),
    "thumbprint": os.getenv("APPSETTING_MSAL_THUMBPRINT")
    }

    msal_app = ConfidentialClientApplication(
    client_id, authority=authority,
    client_credential=msal_credential
    )

    @app.route("test", methods=[func.HttpMethod.GET], auth_level=func.AuthLevel.ANONYMOUS)
    def leads(req: func.HttpRequest) -> func.HttpResponse:

    authorization: str = req.headers.get("Authorization") # type: ignore
    ua = authorization.removeprefix("Bearer ")
    scopes = ["https://sql.azuresynapse-dogfood.net/user_impersonation"]

    result = msal_app.acquire_token_on_behalf_of(ua, scopes)
    if "access_token" not in result:
    body = {
    "result": result
    }
    return func.HttpResponse(json.dumps(body), status_code=500)
    else:
    return func.HttpResponse("{'result': 'success'}", status_code=200)


    The front end is compiled using Browserify.

    {
    "name": "swa",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
    "build": "browserify ./src/index.js -o ./dist/assets/index.js"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "type": "commonjs",
    "dependencies": {
    "@azure/msal-browser": "^4.21.1",
    "browserify": "^17.0.1"
    }
    }


    <!DOCTYPE html>

    <head></head>

    <body>
    <script src="assets/index.js"></script>
    </body>


    const msal = require("@azure/msal-browser");

    // fill these yourself
    const CLIENT_ID = '{client_id}'
    const AUTHORITY = '{authority}'

    const msalConfig = {
    auth: {
    clientId: CLIENT_ID,
    authority: AUTHORITY,
    redirectUri: '/',
    postLogoutRedirectUri: '/'
    },
    cache: {
    cacheLocation: 'localStorage'
    },
    };

    const loginRequest = {
    scopes: [`${CLIENT_ID}/.default`],
    };

    const instance = new msal.PublicClientApplication(msalConfig);

    function setup() {
    const accounts = instance.getAllAccounts();
    if (accounts.length > 0) {
    instance.setActiveAccount(accounts[0]);
    callAPI();
    } else {
    instance.loginRedirect(loginRequest)
    }
    }

    async function callAPI() {
    const response = await instance.acquireTokenSilent({
    ...loginRequest
    }).catch(async (e) => {
    if (e instanceof msal.InteractionRequiredAuthError) {
    await instance.acquireTokenRedirect(loginRequest);
    } else throw e;
    });

    const headers = new Headers()
    const bearer = `Bearer ${response.accessToken}`
    headers.append("Authorization", bearer)

    const options = {
    method: "GET",
    headers: headers
    }

    return fetch('api/test', options)
    .then(async response => response.json())
    .catch(error => {
    console.log(error);
    throw error;
    });
    }

    instance.initialize().then(() => instance.handleRedirectPromise()).then(setup);


    I don't really know how to debug this as no other post I can find has an error with 'iss', only the other fields.

    Continue reading...

Compartilhe esta Página