Search code examples
djangoazure-active-directorymicrosoft-graph-apiazure-ad-b2c

I am getting an error that OAuth2 Code has already been redeemed


def get_access_token(authorization_code):
        
    token_url = "https://login.microsoftonline.com/xxxx-xxxx-xxxx-xxxx-xxxxcxxxxe/oauth2/v2.0/token" 
    redirect_uri = "https://localhost/api/oauth2/callback" 
    client_id = "xxxxxx-xxx-xxxx-xxx-xxxxxx"
    client_secret = "xxxx~xxxxx~xxxx_xxxxxxxxx"
    scope = "https://graph.microsoft.com/.default"


    headers = {'Content-Type' : 'application/x-www-form-urlencoded'}
    token_data = {
        'grant_type': 'client_credentials',
        'code': authorization_code,
        'redirect_uri': redirect_uri,
        'client_id': client_id,
        'client_secret': client_secret,
        'scope': scope,
        }

    response = requests.post(token_url, headers = headers, data=token_data)
    token_json = response.json()

    if 'access_token' in token_json:
        return token_json['access_token']
    else:
        raise Exception(f"Error fetching access token: {token_json.get('error_description', 'Unknown error')}")

I can't find any user information within the bearer that Microsoft provides. The token end point seems to be correct and I get all the user info within Microsoft Graph Explorer but can't on my django app.

Edit:

Changing Grant type to 'authorization_code' gives me error that OAuth2 was already redeemed. I am using Django Auth ADFS library within my Djano App where I think the code is already been redeemed. I cannot directly make changes within the Library. Is there any other way to get the OAuth2 Code?


Solution

  • The error is because you are making use of Authorization code flow but passing parameter value as client credentials for grant_type.

    Initially, I registered Microsoft Entra ID Application in B2C tenant:

    enter image description here

    Granted Delegated type User.Read.All API permission:

    enter image description here

    To resolve the error modify code l:

    import requests
    
    def get_access_token(authorization_code):
        # Replace these with your actual values
        tenant_id = "<tenant_id>"  # Update this with your actual tenant ID
        token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
        redirect_uri = "http://localhost:5000"  # This must match the redirect URI configured in your app registration
        client_id = "<client_id>"  # Your app's client ID
        client_secret = "<client_secret>"  # Your app's client secret
        scope = "https://graph.microsoft.com/.default"  # The scope for your request
    
        # Set up the headers and body for the token request
        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        token_data = {
            'grant_type': 'authorization_code',  # Use authorization_code for this flow
            'code': authorization_code,  # The authorization code received from the redirect
            'redirect_uri': redirect_uri,  # Redirect URI must match the one configured in your app
            'client_id': client_id,  # The app's client ID
            'client_secret': client_secret,  # The app's client secret
            'scope': scope,  # Scopes you want to request
        }
    
        # Make the POST request to get the token
        response = requests.post(token_url, headers=headers, data=token_data)
        
        # Parse the response and check for errors
        token_json = response.json()
    
        # Check if the access token was successfully obtained
        if 'access_token' in token_json:
            return token_json['access_token']
        else:
            error_message = token_json.get('error_description', 'Unknown error')
            raise Exception(f"Error fetching access token: {error_message}")
    
    def get_users(access_token):
        # Initial URL for fetching users from Microsoft Graph API
        graph_url = "https://graph.microsoft.com/v1.0/users"
        headers = {"Authorization": f"Bearer {access_token}"}
        
        users = []
        
        while graph_url:
            response = requests.get(graph_url, headers=headers)
            if response.status_code == 200:
                data = response.json()
                users.extend(data.get("value", []))  # Add the current batch of users
                graph_url = data.get("@odata.nextLink")  # Get the next page link, if it exists
            else:
                raise Exception(f"Error fetching users: {response.status_code} - {response.text}")
        
        return users
    
    def print_users(users):
        # Print user details in a formatted table
        print(f"{'Display Name':<30} {'ID':<40} {'Email':<40}")
        print("-" * 110)
        
        for user in users:
            display_name = user.get("displayName") or "N/A"  # Safely handle None values
            user_id = user.get("id") or "N/A"  # Safely handle None values
            email = user.get("mail") or "N/A"  # Safely handle None values
            print(f"{display_name:<30} {user_id:<40} {email:<40}")
    
    # Example usage
    authorization_code = input("Enter the authorization code: ")
    try:
        # Step 1: Get the access token
        access_token = get_access_token(authorization_code)
        print("Access token :", access_token)
    
        # Step 2: Get the list of users using the access token
        users = get_users(access_token)
    
        # Step 3: Print the users with displayName, id, and mail
        print_users(users)
    except Exception as e:
        print(e)
    
    

    To get code, I ran below authorization request in browser:

    
    https://login.microsoftonline.com/<b2c_tenant_id>/oauth2/v2.0/authorize? 
    &client_id=<app_id>
    &client_secret = <client_secret>
    &redirect_uri= http://localhost:5000
    &response_type=code  
    &response_mode=query  
    &scope= https://graph.microsoft.com/.default
    
    

    enter image description here

    Response:

    enter image description here

    enter image description here