Skip to main content

Command Palette

Search for a command to run...

Step-by-Step Guide to Fetch Outlook Emails Using OAuth for Multi-User Apps

Effortlessly fetch and manage Outlook emails using OAuth authentication.

Updated
16 min read
Step-by-Step Guide to Fetch Outlook Emails Using OAuth for Multi-User Apps

Introduction

Alright, you’re here because you want to fetch emails from multiple Outlook accounts using OAuth, right? Well, buckle up, because we’re about to embark on a wild, code-filled adventure with Python, where you’ll learn how to fetch, refresh, and even hook into email notifications like a true tech wizard. 🔮

We’ll be using Microsoft’s trusty OAuth to authenticate users (no shady shortcuts here), and Python will be our trusty sidekick throughout this journey. I’ll walk you through each step so you can collect all those sweet emails from Outlook users—whether it’s for your application, a secret project, or maybe just to sneak a peek at all those unread messages. (Don't worry, I won’t judge. 😎)

Step 1: Create an Application in Azure Enterprise Applications

Before we can start fetching emails like a pro, we need to tell Azure we’re legit. In other words, we’re going to create an application in Azure Active Directory that will let us interact with Microsoft’s Graph API and access those sweet emails.

Here’s the rundown:

  1. Sign in to Azure Portal: Go to Azure Portal and sign in (you probably already have an account, if not, sign up and get ready to make some magic).

  2. Register Your Application:

    • Navigate to App registrations > New registration.

    • Name your app (maybe something cool like EmailFetcher9000—the more sci-fi the better, right?).

    • Supported account types: This section lets you specify who can use your application. Choose the one that fits your project:

      1. Accounts in this organizational directory only: Choose this if your app will only be used by users within your organization (i.e., single tenant).

      2. Accounts in any organizational directory: Choose this if your app should be available to users in any Azure AD tenant (i.e., multitenant, like for businesses or companies outside your organization).

      3. Accounts in any organizational directory and personal Microsoft accounts: Select this if you want your app to be available to anyone with a Microsoft account, like Skype or Xbox users, in addition to organizational users.

      4. Personal Microsoft accounts only: If you only want your app to be available to users with a personal Microsoft account, like those using Outlook, OneDrive, or Xbox, choose this option.

    • Under Redirect URI, leave it empty for now (we’ll fill it in later).

  3. Create Client Secret:

    • Go to Certificates & secrets > New client secret.

    • Add a description and set an expiry date (we want it to last long enough to test, like 1 year).

    • Click Add and copy that client secret value. Now, save it somewhere safe—this is a one-time thing. Once you leave this page, you won’t be able to see it again. (Trust me, you’ll thank me later! 🔐)

  4. Back at the Overview page, grab your Application (client) ID and Directory (tenant) ID. You’ll need these to authenticate and identify your app. These values are your gateway to the email-fetching world!

Step 2: Create a Sign-In URL (Time to Let Users Log In)

Alright, we’ve got our application all set up in Azure, and now it’s time to get the users involved. We need to send them a URL where they can authenticate with Microsoft and give us permission to access their emails. This URL is your golden ticket to OAuth land. 🎫

To do this, we’re going to use a Python library called MSAL (Microsoft Authentication Library). It’s super handy for managing OAuth and talking to Microsoft’s identity services. So let’s install it, and then we’ll use it to create the sign-in URL.

  1. Install MSAL: First, install the msal library if you haven’t already:

     pip install msal
    

The Code to Create the URL: Now, let’s write some Python magic to create the sign-in URL. You’ll need to pass in your client ID, client secret, tenant ID, and scopes (which define the permissions your app is asking for). For now, we’ll stick to the Mail.Read scope because we just want to fetch emails. If you want to do more, you can always add extra scopes later.

Here’s how you’ll create the URL:

import msal
import uuid

CLIENT_ID = ""  # Your app's client ID
CLIENT_SECRET = ""  # Your app's client secret
TENANT_ID = ""  # Your tenant ID
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"
SCOPES = ["Mail.Read"]  # offline_access gives refresh tokens

# Generate a unique state (this could be a user ID or session ID)
state = str(uuid.uuid4())  # Random state value to track the user

# Set up MSAL app
app = msal.ConfidentialClientApplication(CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET)

# Generate Login URL with the state parameter
login_url = app.get_authorization_request_url(SCOPES, redirect_uri="http://localhost:5000/callback", state=state)
print(f"Go to this URL to authenticate:\n{login_url}")

When generating the sign-in URL, we can include a unique state value that represents the user (like a session ID or user ID). This value will be sent back to your callback URL when the user finishes authenticating, and you can extract it to match the authorization code to the correct user.

  1. What Happens Next?

    • When users visit the generated URL, they’ll be asked to log in to their Microsoft account and grant the necessary permissions (in this case, read access to their emails).

    • Once they approve, Microsoft will redirect them back to your app at the redirect URI (http://localhost:5000/callback in this case) with a special authorization code and the state value to identify the user.

That’s it! You’ve now got a sign-in URL ready for users to authenticate. Simple, right? But here comes the real fun—exchanging that code for tokens in the next step. 🔄

Step 3: Use the Code to Create a Token (The Key to Your Emails)

After the user has authenticated and granted permission, you’ll get an authorization code that we need to exchange for an access token. This token is like your golden key 🗝️ to access the user's mailbox and fetch those emails.

But before we go all in, remember: You’ll also get a refresh token, which is like a backup key that lets you get a fresh access token when the current one expires. So, it's crucial to store both tokens securely. 📦

The Code to Exchange the Authorization Code:

Let’s update our Python code to exchange the authorization code for an access token (and refresh token).

Here’s how we do it:

import msal
import json

CLIENT_ID = ""  # Your app's client ID
CLIENT_SECRET = ""  # Your app's client secret
TENANT_ID = ""  # Your tenant ID
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"
SCOPES = ["Mail.Read"]  # Permissions for reading emails

# Set up MSAL app
msal_app = msal.ConfidentialClientApplication(
    CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET
)

@app.route('/callback')
def callback():
    # Step 1: Retrieve the authorization code and state from the callback request
    code = request.args.get("code")
    state = request.args.get("state")  # Remember that `state` was generated earlier to track the user

    # Here, you can verify the state against your session or database if needed.

    # Step 2: Exchange the authorization code for tokens
    result = msal_app.acquire_token_by_authorization_code(
        code,
        SCOPES,  # We want the Mail.Read permission
        redirect_uri="http://localhost:5000/callback"
    )

    if "access_token" in result:
        # Step 3: Store the tokens securely (in this case, save them in a JSON file)
        with open(f"{state}_tokens.json", "w") as f:
            json.dump(result, f)
        return "Authentication successful! You can close this window."
    else:
        return f"Authentication failed: {result.get('error_description')}", 400

What’s Happening Here:

  1. The Callback: Once the user is redirected back to your callback URL, you’ll grab the authorization code from the query parameters.

  2. Exchanging the Code: The MSAL library then uses that code and the registered scopes to exchange it for an access token and refresh token. The tokens will be returned as a JSON object.

  3. Storing Tokens: Here, we’re storing the tokens in a file named after the state parameter (for unique user tracking). You should store tokens securely, but for now, we’re using a simple JSON file. 🔒

  4. Success or Failure: If everything goes smoothly, we’ll show a success message and store the tokens. If something fails, we’ll show an error message.

Key Notes:

  • The access token allows you to call Microsoft Graph API to fetch emails. You need this token for every request.

  • The refresh token is a backup plan. If your access token expires, you can use this to get a new access token without requiring the user to authenticate again. We’ll handle this part in the next step.

Now we’ve got our tokens in hand! 🎉 Ready to use them to fetch emails?

Step 4: Use the Token to Fetch Emails Periodically (The Fun Part)

Alright, now comes the magic. We’ll use the access token we got in the last step to make requests to the Microsoft Graph API and get all the juicy details about the user's emails. 📨

We’ll make a GET request to https://graph.microsoft.com/v1.0/me/messages, and in return, we’ll get a list of all the emails in the user's inbox.

Fetching Emails:

Here’s the Python code to fetch the emails from the user's inbox:

import requests
import json

# Load the tokens (you’ve saved them in the last step)
with open("tokens.json", "r") as f:
    tokens = json.load(f)

# Get the access token from the stored tokens
access_token = tokens["access_token"]

# Set up the Authorization header
headers = {
    "Authorization": f"Bearer {access_token}"
}

# Make the request to the Microsoft Graph API to fetch emails
response = requests.get("https://graph.microsoft.com/v1.0/me/messages", headers=headers)

# If the request is successful, you'll get the emails
if response.status_code == 200:
    emails = response.json()
    for email in emails.get("value", []):
        print(f"📧 Subject: {email['subject']}")
        print(f"⏰ Received: {email['receivedDateTime']}")
        print(f"From: {email['from']['emailAddress']['address']}")
        print(f"-----------------------------------------")
else:
    print(f"Failed to fetch emails: {response.status_code}")
    print(response.json())

What’s Happening Here:

  1. Authorization Header: You’re passing the access token in the Authorization header to authenticate the request to Microsoft Graph API.

  2. Make the Request: The requests.get() call fetches the emails from the user’s inbox using the me/messages endpoint. This will return a JSON response with all the emails.

  3. Parsing the Response: The response contains a list of emails, and we loop through each one to print out some basic details like the subject, received date, and sender.

  4. Error Handling: If something goes wrong (like the token expires), we print out the error code and the response to help debug.

Why Periodic Fetching is Important:

You probably don’t want to call the Graph API every time someone visits your app (because that’s a lot of unnecessary calls). Instead, you could set up a periodic task (like a cron job or a background task) to fetch the emails every few minutes or so. This way, you stay up to date with the latest emails without spamming Microsoft’s servers.

Key Points to Remember:

  • Access Token: You use the access token to authenticate and make requests to the API.

  • Email Info: The email response gives you all the details like subject, received date, and sender, but you can easily tweak it to fetch more or less information.

  • Error Handling: Always check for errors (e.g., expired tokens), and handle them gracefully to ensure smooth app operation.

  • Periodicity: Set up periodic fetching, so you’re always up to date with new emails.

This is the part where your app comes alive! 🎉 Are you ready for the next step, where we talk about refreshing those tokens when they expire?

Step 4.1: Refresh Tokens (Never Let the Magic Stop)

Access tokens aren’t forever (sadly), and once they expire, Microsoft Graph will deny you access to the emails. But don’t panic! That’s where the refresh token comes in — it’s like a backup key that lets you get a new access token without the user having to log in again. 🔄

Let’s say the access token expires after an hour, but you can use the refresh token to get a new one. The best part? This can happen silently in the background.

The Code to Refresh the Token:

Here’s how you can implement token refreshing in your app:

pythonCopyEditimport msal
import json

# Load tokens
with open("tokens.json", "r") as f:
    tokens = json.load(f)

# Set up the MSAL app again (since we need it for refreshing the token)
msal_app = msal.ConfidentialClientApplication(
    CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET
)

def refresh_access_token():
    # Get the refresh token from the saved tokens
    refresh_token = tokens["refresh_token"]

    # Attempt to acquire a new access token using the refresh token
    result = msal_app.acquire_token_by_refresh_token(refresh_token, SCOPES)

    if "access_token" in result:
        # If successful, save the new tokens (including the new refresh token)
        with open("tokens.json", "w") as f:
            json.dump(result, f)
        return result["access_token"]
    else:
        raise Exception("Failed to refresh token!")

# Use the refreshed access token
access_token = refresh_access_token()
print("Successfully refreshed the access token!")

What’s Happening Here:

  1. Load Tokens: We load the saved tokens from the tokens.json file. This file should contain both the access token and refresh token from the last time the user authenticated.

  2. Set Up MSAL App: We initialize the MSAL app again (because we need it to handle the refresh process).

  3. Refresh the Token: The function acquire_token_by_refresh_token() takes the refresh token and the requested scopes and returns a new set of tokens (including a fresh access token).

  4. Save the New Tokens: After refreshing the tokens, we overwrite the tokens.json file with the new access token and refresh token. This ensures that you’re always working with fresh tokens.

  5. Use the New Token: The returned access token is now fresh and ready to be used to make requests to the Microsoft Graph API (to fetch those emails!).

Token Expiry & Revocation (Don’t Forget This!)

Now that you’ve got your token refreshing down, it’s important to keep in mind that tokens don’t last forever, and they can get revoked in some scenarios. While our app is pretty solid at refreshing tokens when they expire, there are a few situations where things might get tricky (like password changes or security policies).

So, if you're building a production application, make sure to check out Microsoft’s official documentation to get all the details on token lifetimes and revocation scenarios. It’s an absolute must-read to handle edge cases perfectly and make sure your app doesn’t hit any unexpected roadblocks. 📜

🔗 Token Lifetime & Revocation Documentation

Trust me, you’ll thank me later when you’re dealing with all the real-world scenarios. 😎

Key Points to Remember:

  • Refresh Token: The refresh token is your lifeline when the access token expires. You don’t need to ask the user to log in again, just refresh the token!

  • Automatic Refreshing: You should periodically check if the access token is expired and refresh it as needed. This could be done every time you attempt to make an API call or on a regular schedule.

  • Storage: Always store both tokens securely — preferably in an encrypted format (but for now, we’re using JSON for simplicity).

  • Error Handling: If the refresh fails (like if the refresh token is expired or revoked), you should have a backup plan, such as asking the user to authenticate again.

Now your app can keep fetching emails without worrying about token expiration! 🚀 We’re getting close, but there’s one last thing to cover: setting up webhook subscriptions to notify your app when a new email arrives! 😎

[OPTIONAL] Step 5: Setting Up Webhook Subscriptions (Get Notified Instantly)

A webhook is a great way to automatically notify your app when a new email arrives in the inbox, instead of constantly checking for new messages. We’ll use the Microsoft Graph API to create a subscription that sends a notification to your app whenever a new email is received. 🚀

The Code to Create a Webhook Subscription:

First, you’ll need to create a subscription by making an HTTP request to the Microsoft Graph API. The API will send notifications to the webhook URL you provide whenever there’s a new email.

pythonCopyEditimport requests
import json
from datetime import datetime, timedelta

# Load tokens
with open("tokens.json", "r") as f:
    tokens = json.load(f)

access_token = tokens["access_token"]

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

WEBHOOK_URL = "http://localhost:5000/webhook"  # Replace with your webhook URL
EXPIRES_IN = (datetime.utcnow() + timedelta(minutes=4230)).isoformat() + "Z"  # Max 4230 minutes (~3 days)

# Create the subscription data
subscription_data = {
    "changeType": "created",  # Only notify when a new email is created
    "notificationUrl": WEBHOOK_URL,
    "resource": "me/messages",  # Listen for new messages
    "expirationDateTime": EXPIRES_IN,
    "clientState": "your_secret_token"  # Optional: a secret token to verify notifications
}

# Make the request to create the subscription
response = requests.post("https://graph.microsoft.com/v1.0/subscriptions", headers=headers, json=subscription_data)
print(response.json())  # Print the response to verify if the subscription was created

What’s Happening Here:

  1. Load Tokens: You’ll load the tokens you saved earlier from the tokens.json file.

  2. Prepare Webhook URL: We define the URL where Microsoft will send the webhook notifications. In this example, we’re using http://localhost:5000/webhook, but you should replace it with your own webhook URL (make sure it's publicly accessible).

  3. Set Expiration Time: The subscription needs an expiration time, which cannot exceed 4230 minutes (about 3 days). So, we set the expiration time to 3 days from the current time.

  4. Create the Subscription: We define the subscription parameters:

    • changeType: This specifies what kind of events we want to listen for. In our case, it's created, meaning we only want to be notified when new emails arrive.

    • resource: We're subscribing to the me/messages resource, which refers to the user’s email messages.

    • clientState: This is an optional parameter that you can use to verify the authenticity of the notification later on.

  5. Send the Request: We send the subscription request to the Microsoft Graph API. If everything goes smoothly, you’ll get a confirmation response.

Key Takeaways:

  • Real-time Notifications: Webhooks are the best way to get real-time updates when new emails arrive, instead of constantly polling the API.

  • Webhook URL: Make sure your app has an endpoint that’s capable of receiving POST requests from Microsoft Graph — this is where the notifications will be sent.

  • Expiration Time: Your subscription will expire after 3 days (4230 minutes). Don’t worry, we’ll handle renewing the subscription in the next step! 😉

Step 5.1: Renewing the Subscription (Keep the Notifications Coming)

After 3 days (4230 minutes), your webhook subscription will expire. Don't panic! We can easily renew the subscription to keep receiving notifications. You can set this up by making a request to update the subscription's expiration time.

Here's how we can renew the subscription using Python:

The Code to Renew the Subscription:

import requests
import json
from datetime import datetime, timedelta

# Load tokens
with open("tokens.json", "r") as f:
    tokens = json.load(f)

access_token = tokens["access_token"]

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

# Renewing the subscription (extend the expiration time)
def renew_subscription(subscription_id):
    new_expiry = (datetime.utcnow() + timedelta(minutes=4230)).isoformat() + "Z"  # Max 4230 minutes

    # Create the data to update the subscription
    update_data = {"expirationDateTime": new_expiry}

    # Send the request to update the subscription
    response = requests.patch(f"https://graph.microsoft.com/v1.0/subscriptions/{subscription_id}",
                              headers=headers, json=update_data)

    print(response.json())  # Check the response to ensure it was successful

# Subscription ID: This should be the ID returned when you created the subscription
subscription_id = "your-subscription-id"
renew_subscription(subscription_id)

What’s Happening Here:

  1. Load Tokens: As before, we load the access token from the tokens.json file to authenticate the request.

  2. Renewing the Subscription: We use the PATCH method to update the expiration date of the subscription. The new expiration time is set to another 4230 minutes (3 days).

  3. Subscription ID: Each subscription has a unique ID, which you should save when you first create the subscription. When it’s time to renew, we use that ID to specifically update the right subscription.

  4. Response: The Microsoft Graph API will respond with information about the updated subscription. If everything is good, you’ll get a confirmation.

Key Takeaways:

  • Subscription Renewal: Webhook subscriptions can be renewed to keep them alive, but remember, they only last for 3 days at a time.

  • Subscription ID: You need to store the subscription_id when you create the subscription. This is required when renewing the subscription.

And That’s It! Real-time Email Notifications Forever!

With subscription creation and renewal in place, your app will stay fully connected and up-to-date with all incoming emails in real-time! 🎉

Conclusion: You’re All Set to Fetch Emails Like a Pro! 🎉

And there you have it, folks! We've been through every single step needed to create a killer email-fetching app that uses OAuth with Microsoft Outlook and keeps things running smoothly with real-time email notifications.

Before you dash off to build the next big thing, make sure you’re reading up on token expiration and revocation (especially if you're deploying this in production). It's important to understand how to handle those edge cases where Microsoft might revoke access due to a password change or compromised account. I linked to the official documentation to keep you in the know. 💡

So, grab your tokens, set up that webhook, and get ready for your app to handle emails like a pro! 📧

And remember, with this setup, your app will be as smooth as butter – effortless email fetching and real-time notifications. 🎯

Now go forth and build awesome things! 💻🚀 Happy coding, and if you hit any roadblocks, I’m here to help. Let’s make email fetching fun and funky! 😎

84 views