How to allow Microsoft 365 (Office 365) users to update their own profile?

Short answer: Microsoft does not allow users update their profiles, but I will show you a workaround.

Introduction

In today’s modern workplace, it is crucial for employees to have the ability to easily manage and update their own profile information within their organization’s systems. While Microsoft 365 offers a wide range of features and tools for collaboration, it can sometimes be challenging for users to update their personal information, such as phone numbers, cities, and other details.

In this blog post, I will explore how to create a simple Flask web application that enables Microsoft 365 users to update their own profile information. My solution will utilize the Microsoft Graph API for interacting with Microsoft 365 and managing user profiles. This custom-built application will not only make it easier for users to maintain their own information but also reduce the workload of administrators who would otherwise have to manage these updates manually.

Disclaimer: This blog post and the provided code are intended for demonstration purposes and to serve as a proof of concept only. While efforts have been made to provide accurate and reliable information, I assumes no responsibility or liability for any errors, omissions, or for the results obtained from the use of this information. It is the responsibility of the user to ensure the code’s appropriateness for their specific needs, and to implement any necessary security measures and error handling to meet their own requirements. By using the code provided in this blog post, you acknowledge that you are doing so at your own risk, and I shall not be held responsible for any issues or damages arising from its use.

Prerequisites

Before diving into the development of the Flask web application, make sure you have the following prerequisites:

  1. A Microsoft 365 subscription: You’ll need access to a Microsoft 365 tenant with an administrator account to set up the App Registration and grant the necessary permissions for the application.
  2. Python 3.x: I assume that you have Python 3.x installed on your machine. You can download the latest version of Python from the official website: https://www.python.org/downloads/
  3. Flask and other required Python libraries: You’ll need Flask, Authlib, Flask-Session, and requests libraries. You can install these packages using pip:
pip install Flask Authlib Flask-Session requests

  1. Docker (optional): If you wish to deploy the application using Docker, make sure you have Docker installed on your machine. Download and install Docker from the official website: https://www.docker.com/get-started
  2. A text editor or Integrated Development Environment (IDE) of your choice: You’ll need a text editor or IDE to write and edit the Python code for the Flask web application.
  3. Basic understanding of Python, Flask, Microsoft Graph API, and OAuth 2.0: Familiarity with these technologies will help you better understand the concepts and steps in this tutorial.

With these prerequisites in place, you’re ready to start building the application.

Building the Flask Application

Let’s start coding! All mentioned code bellow you can found in my GitHub repository.

The application will use the Microsoft Graph API to interact with the user’s profile data. We will explain how to write the code step-by-step, including explanations for each function.

Create a new file named “app.py” and start by importing the required libraries:

from flask import Flask, redirect, url_for, session, request, render_template
from flask_session import Session
from authlib.integrations.flask_client import OAuth
import os
import requests

Set up the Flask app configuration and the OAuth object for Microsoft Graph:

app = Flask(__name__)
app.secret_key = 'your_secret_key'  # Replace with a secure secret key
app.config['SESSION_TYPE'] = 'filesystem'
app.config["SESSION_PERMANENT"] = False
Session(app)
tenant_name = os.environ['TENANT_NAME']

Write the get_app_access_token function to obtain an app-only access token using the client credentials grant flow:

def get_app_access_token(client_id, client_secret, tenant_name):
    token_url = f'https://login.microsoftonline.com/{tenant_name}/oauth2/v2.0/token'

    token_data = {
        'grant_type': 'client_credentials',
        'client_id': client_id,
        'client_secret': client_secret,
        'scope': 'https://graph.microsoft.com/.default'
    }

    token_r = requests.post(token_url, data=token_data)
    token_r.raise_for_status()
    token_response = token_r.json()

    return token_response['access_token']

Configure the OAuth object for Microsoft Graph:

oauth = OAuth(app)
msgraph = oauth.register(
    'msgraph',
    client_id=os.environ['CLIENT_ID'],
    client_secret=os.environ['CLIENT_SECRET'],
    access_token_url='https://login.microsoftonline.com/' + tenant_name + '/oauth2/v2.0/token',
    authorize_url='https://login.microsoftonline.com/' + tenant_name + '/oauth2/v2.0/authorize',
    api_base_url='https://graph.microsoft.com/v1.0/',
    server_metadata_url='https://login.microsoftonline.com/' + tenant_name + '/v2.0/.well-known/openid-configuration',
    client_kwargs={'scope': 'User.Read Directory.AccessAsUser.All'},
)

Define the index function to handle the root route (“/”) of the application. If the user has an access token stored in the session, it redirects the user to the profile page. Otherwise, it redirects them to the login page:

@app.route('/')
def index():
    if 'access_token' in session:
        return redirect(url_for('profile'))
    else:
        return redirect(url_for('login'))

Define the login function to initiate the OAuth 2.0 authorization flow by redirecting the user to the Microsoft authorization endpoint:

@app.route('/login')
def login():
    redirect_uri = url_for('authorize', _external=True)
    return msgraph.authorize_redirect(redirect_uri)

Define the authorize function to handle the OAuth 2.0 callback. After the user authorizes the application, they are redirected to this route with an authorization code. The application exchanges the code for an access token and stores it in the session:

@app.route('/authorize')
def authorize():
    session['access_token'] = msgraph.authorize_access_token()
    user_data = msgraph.get('me').json()
    session['user_id'] = user_data['id']
    return redirect(url_for('profile'))

Define the profile function to handle the profile route (“/profile”). It displays the user’s profile information and allows them to update it. If the user submits the form, the application checks if they are updating their own profile and then sends a PATCH request to the Microsoft Graph API with the app-only access token:

@app.route('/profile', methods=['GET', 'POST'])
def profile():
    if 'access_token' not in session:
        return redirect(url_for('login'))

    if request.method == 'POST':
        # Retrieve form data and check if the user is updating their own profile
        # ...

        # Update the profile using the app-only access token
        access_token = get_app_access_token(os.environ['CLIENT_ID'], os.environ['CLIENT_SECRET'], os.environ['TENANT_NAME'])

        headers = {
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json'
        }

        data = {
            # ... profile data from the form ...
        }
        response = requests.patch(f'https://graph.microsoft.com/v1.0/users/{session["user_id"]}', headers=headers, json=data)
        response.raise_for_status()
        if response.status_code == 204:
            return redirect(url_for('profile'))
        else:
            return response.text
    user_data = msgraph.get('me?$select=id,displayName,mail,country,state,city,postalCode,streetAddress,companyName,department,jobTitle,officeLocation,mobilePhone', token=session['access_token']).json()

    return render_template('profile.html', user_data=user_data)

Finally, start the Flask development server:

if __name__ == '__main__':
    app.run(debug=True, port="50000")

Now, let’s create the profile.html template that will display the user’s profile information and allow them to update it. This template will be rendered by the profile function in the Flask application.

Create a new HTML file named profile.html in a folder called templates within your application’s directory.

Start by defining the HTML document structure with a <!DOCTYPE>, <html>, <head>, and <body> tags:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>User Profile</title>
</head>
<body>
    <!-- The content of the page will go here -->
</body>
</html>

Inside the <body> tag, display the user’s display name and email using the Jinja2 template engine:

<h1>{{ user_data.displayName }}</h1>
<h3>Email: {{ user_data.mail }}</h3>

Create a <form> element with a POST method that submits the form data to the profile route:

<form action="{{ url_for('profile') }}" method="post">
    <!-- The form fields will go here -->
</form>

Inside the form, add a hidden input field to store the user’s ID. This will be used to verify if the user is updating their own profile:

<input type="hidden" name="user_id" value="{{ user_data['id'] }}">

For each profile field, create a <label> and an <input> element. Use the Jinja2 template engine to pre-fill the input fields with the user’s current data:

<label for="country">Country:</label>
<input type="text" id="country" name="country" value="{{ user_data.country }}"><br><br>
<!-- Repeat for other fields: state, city, postalCode, streetAddress, companyName, department, jobTitle, officeLocation, and mobilePhone -->

Finally, add a submit button to the form:

<input type="submit" value="Update">

Configuring the Microsoft 365 App Registration

Before your Flask application can interact with the Microsoft Graph API, you need to register an application in the Microsoft 365 Azure Active Directory. This registration process generates the necessary credentials and configures the required permissions for your app.
Follow these steps to configure the Microsoft 365 App Registration:

  1. Sign in to the Azure Portal using your Microsoft 365 administrator account.
  2. Click on “Azure Active Directory” from the left-hand menu, and then click on “App registrations”.
  3. Click on the “New registration” button at the top of the page.
  4. Fill in the following information:
    • Name: Provide a descriptive name for your application, such as “Flask MS Graph App”.
    • Supported account types: Choose “Accounts in this organizational directory only” to restrict access to users within your organization.
    • Redirect URI: Select “Web” from the dropdown and enter a placeholder URL (e.g., http://localhost:50000/authorize). This value will be replaced later with the actual redirect URI generated by your Flask app.
  5. Click on the “Register” button to create the app registration.
  6. Once the app registration is created, you’ll be redirected to the app’s “Overview” page. Here, you can find important information like the Application (client) ID and Directory (tenant) ID. Make a note of these values, as you’ll need them later.
  7. Go to the “Certificates & secrets” section under “Manage” in the left-hand menu. Click on the “New client secret” button, provide a description, and choose an expiration period. Click “Add” to create the client secret. Make sure to copy the generated client secret value, as you won’t be able to see it again.
  8. Next, go to the “API permissions” section under “Manage” in the left-hand menu. Click on the “Add a permission” button, and then select “Microsoft Graph” from the list of available APIs.
  9. In the “Request API permissions” pane, switch to the “Delegated permissions” tab. Search for the “User.Read” and “Directory.AccessAsUser.All” permissions and check the box next to both of them.
  10. Switch to the “Application permissions” tab. Search for the “Directory.ReadWrite.All” permission under the “Directory” category and check the box next to it. Click on the “Add permissions” button to add the selected permission to your app.
  11. Finally, click on the “Grant admin consent for [Your Organization]” button in the “API permissions” section. This will grant the necessary permissions for your app to use the Microsoft Graph API to read and write user profiles in your organization.
  12. The result should looks like on a screenshot bellow.

Now, your Microsoft 365 App Registration is configured with the required credentials and permissions. You’ll need to use the Application (client) ID, Directory (tenant) ID, and client secret in your Flask application to authenticate and interact with the Microsoft Graph API.

Running the app

First of all, set the following environment variables with the appropriate values from your App Registration what we created on previous step:

export CLIENT_ID=your_client_id
export CLIENT_SECRET=your_client_secret
export TENANT_NAME=your_tenant_name

Run the Flask application:

python app.py

Now you can access the application in your web browser by navigating to “http://localhost:50000“.

Conclusion

In this blog post, we walked through the process of building a Flask application that allows users to update their Microsoft 365 profiles using the Microsoft Graph API. We started by setting up the app’s environment and implementing the necessary Flask routes and functions. We then discussed how to create and configure the required Microsoft 365 App Registration, enabling our Flask app to interact with the Microsoft Graph API securely. Lastly, we explored the creation of an HTML template for displaying and updating user profile information.

By following this guide, you should now have a better understanding of how to leverage the Microsoft Graph API in your Flask applications to interact with Microsoft 365 user data. You can further extend this application by implementing additional features, such as updating other user attributes or managing group memberships. The Microsoft Graph API offers a vast array of possibilities for enhancing your applications and automating tasks within the Microsoft 365 ecosystem.

As usual, all mentioned code you can found in my GitHub repository.

Happy coding!

Useful links about Microsoft Graph API

  1. Microsoft Graph API Overview – This is the official documentation of the Microsoft Graph API, providing an overview of its features and capabilities.
  2. Microsoft Graph API Reference – This is the reference documentation for the Microsoft Graph API, detailing the available endpoints, request types, and responses.
  3. Microsoft Graph API Samples and Tutorials – This page contains various samples and tutorials demonstrating how to use the Microsoft Graph API in different scenarios.
  4. Microsoft Graph API GitHub Repository – This is the official GitHub repository for the Microsoft Graph organization, containing various projects, samples, and resources related to the Graph API.
  5. Microsoft Graph API Explorer – This is an interactive tool that allows you to explore the Microsoft Graph API by submitting requests and viewing responses directly from your browser.
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments