Install and configure OneNote SDK/API authentication with delegated auth (MSAL).
Use when setting up a new OneNote integration, configuring Azure AD app registration, or migrating from deprecated app-only auth.
Trigger with "install onenote", "setup onenote auth", "onenote credentials", "azure ad onenote".
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Install and configure OneNote SDK/API authentication with delegated auth (MSAL).
Use when setting up a new OneNote integration, configuring Azure AD app registration, or migrating from deprecated app-only auth.
Trigger with "install onenote", "setup onenote auth", "onenote credentials", "azure ad onenote".
allowed-tools
Read, Write, Edit, Bash(npm:*), Bash(pip:*), Grep
version
1.6.0
license
MIT
author
Jeremy Longshore <jeremy@intentsolutions.io>
tags
["saas","onenote","microsoft"]
compatibility
Designed for Claude Code
OneNote Install & Auth
Overview
Set up Microsoft Graph API authentication for OneNote using delegated credentials via MSAL. This skill walks through Azure AD app registration, SDK installation, permission scope selection, token caching, and connection verification for both Python and TypeScript.
BREAKING CHANGE (March 31, 2025): App-only authentication (ClientSecretCredential) was deprecated for OneNote APIs. All integrations MUST use delegated auth — DeviceCodeCredential or InteractiveBrowserCredential. If your existing code uses ClientSecretCredential with OneNote endpoints, it will receive 403 Forbidden on every call. This skill provides the correct migration path.
Prerequisites
Azure account with permission to register applications (Azure AD admin or Application Developer role)
# .env file — NEVER commit this to version control
AZURE_CLIENT_ID=your-application-client-id
AZURE_TENANT_ID=your-directory-tenant-id
# Do NOT set AZURE_CLIENT_SECRET — app-only auth is deprecated for OneNote
Step 5: Authenticate with Python (DeviceCodeCredential)
import os
from azure.identity import DeviceCodeCredential
from msgraph import GraphServiceClient
CLIENT_ID = os.environ["AZURE_CLIENT_ID"]
TENANT_ID = os.environ["AZURE_TENANT_ID"]
# DeviceCodeCredential prompts user to visit a URL and enter a code# This is the recommended flow for CLI tools and headless environments
credential = DeviceCodeCredential(
client_id=CLIENT_ID,
tenant_id=TENANT_ID,
)
scopes = ["Notes.ReadWrite"]
client = GraphServiceClient(credentials=credential, scopes=scopes)
# Verify connection
notebooks = await client.me.onenote.notebooks.get()
if notebooks and notebooks.value:
for nb in notebooks.value:
print(f"Notebook: {nb.display_name} (id: {nb.id})")
else:
print("No notebooks found — connection succeeded but account has no notebooks")
Step 6: Authenticate with TypeScript (DeviceCodeCredential)
Without token caching, users must re-authenticate on every run. MSAL supports persistent token caching:
import { PublicClientApplication } from"@azure/msal-node";
import * as fs from"fs";
constCACHE_PATH = "./.msal-token-cache.json";
const pca = newPublicClientApplication({
auth: {
clientId: process.env.AZURE_CLIENT_ID!,
authority: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}`,
},
});
// Load cached tokens on startupconst cache = pca.getTokenCache();
if (fs.existsSync(CACHE_PATH)) {
cache.deserialize(fs.readFileSync(CACHE_PATH, "utf-8"));
}
// After acquiring a token, persist the cacheconst result = await pca.acquireTokenByDeviceCode({
scopes: ["Notes.ReadWrite"],
deviceCodeCallback: (response) =>console.log(response.message),
});
fs.writeFileSync(CACHE_PATH, cache.serialize());
// Add .msal-token-cache.json to .gitignore immediately
Step 8: Multi-Tenant vs Single-Tenant
Configuration
Authority URL
When to use
Single tenant
https://login.microsoftonline.com/{tenant-id}
Internal enterprise tools
Multi-tenant
https://login.microsoftonline.com/common
SaaS apps serving multiple orgs
Personal accounts
https://login.microsoftonline.com/consumers
Consumer OneNote only
For multi-tenant apps, replace TENANT_ID in the authority URL with common or organizations.
Output
After completing these steps you will have:
An Azure AD application registered with correct OneNote permissions
Working authentication using delegated credentials (DeviceCodeCredential)
Token caching for persistent sessions without re-authentication
A verified Graph API connection that lists the user's notebooks
Error Handling
Error
Code
Root Cause
Solution
AADSTS7000218
403
App configured for app-only auth
Reconfigure for delegated auth — app-only was deprecated March 2025
Insufficient privileges
403
Missing or wrong permission scope
Add Notes.ReadWrite in Azure Portal, then re-consent
AADSTS50011
400
Redirect URI mismatch
Set redirect URI to http://localhost for device code flow
Token expired
401
Access token has expired (1 hour default)
Implement token refresh or use MSAL's built-in token cache
AADSTS700016
400
Application not found in tenant
Verify CLIENT_ID matches the registered app
InteractionRequired
—
Cached token invalid, need re-auth
Clear .msal-token-cache.json and re-authenticate
Diagnosing 403 errors: If you get 403 after March 2025 on code that previously worked, check whether your credential uses ClientSecretCredential. This is the single most common cause — app-only auth for OneNote is permanently deprecated. Switch to DeviceCodeCredential.
Examples
Quick connectivity test (Python one-liner):
# Paste into Python REPL after setting AZURE_CLIENT_ID and AZURE_TENANT_IDfrom azure.identity import DeviceCodeCredential; from msgraph import GraphServiceClient
c = GraphServiceClient(credentials=DeviceCodeCredential(client_id="YOUR_ID", tenant_id="YOUR_TENANT"), scopes=["Notes.Read"])
print(await c.me.onenote.notebooks.get())
Switching from deprecated app-only auth:
// BEFORE (broken after March 2025):// const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);// AFTER (correct):const credential = newDeviceCodeCredential({ clientId, tenantId });