Agent Skills: OneNote Install & Auth

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/onenote-install-auth

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/onenote-pack/skills/onenote-install-auth

Skill Files

Browse the full folder contents for onenote-install-auth.

Download Skill

Loading file tree…

plugins/saas-packs/onenote-pack/skills/onenote-install-auth/SKILL.md

Skill Metadata

Name
onenote-install-auth
Description
|

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)
  • Node.js 18+ or Python 3.10+
  • Access to Azure Portal App Registrations
  • A OneNote account (personal Microsoft account or Microsoft 365 work/school account)

Instructions

Step 1: Register an Azure AD Application

  1. Navigate to Azure Portal > App Registrations
  2. Click New registration
  3. Set the Name (e.g., onenote-integration-dev)
  4. Under Supported account types, choose:
    • Single tenant — only your organization (most restrictive, recommended for internal tools)
    • Multi-tenant — any Azure AD directory (needed if serving multiple orgs)
    • Multi-tenant + personal — includes personal Microsoft accounts (needed if targeting consumer OneNote)
  5. Under Redirect URI, select Public client/native and set URI to http://localhost
  6. Click Register and note the Application (client) ID and Directory (tenant) ID

Step 2: Configure API Permissions

  1. In your app registration, go to API permissions > Add a permission > Microsoft Graph > Delegated permissions
  2. Add the appropriate scope:

| Scope | Use Case | |-------|----------| | Notes.Read | Read-only access to user's notebooks | | Notes.ReadWrite | Read and write to user's notebooks | | Notes.ReadWrite.All | Read/write all notebooks the user can access (including shared) | | Notes.Read.All | Read all notebooks the user can access (including shared) |

  1. Click Grant admin consent if you have admin rights (otherwise users see a consent prompt on first login)

Step 3: Install SDKs

Python:

pip install msgraph-sdk azure-identity

TypeScript/Node:

npm install @microsoft/microsoft-graph-client @azure/identity @azure/msal-node

Step 4: Configure Environment Variables

# .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)

import { Client } from "@microsoft/microsoft-graph-client";
import { TokenCredentialAuthenticationProvider } from
  "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials";
import { DeviceCodeCredential } from "@azure/identity";

const credential = new DeviceCodeCredential({
  clientId: process.env.AZURE_CLIENT_ID!,
  tenantId: process.env.AZURE_TENANT_ID!,
  userPromptCallback: (info) => {
    // Display the device code login instructions to the user
    console.log(info.message);
  },
});

const scopes = ["Notes.ReadWrite"];

const authProvider = new TokenCredentialAuthenticationProvider(credential, {
  scopes,
});

const client = Client.initWithMiddleware({ authProvider });

// Verify connection
const notebooks = await client.api("/me/onenote/notebooks").get();
console.log(`Found ${notebooks.value.length} notebooks`);
for (const nb of notebooks.value) {
  console.log(`  - ${nb.displayName} (${nb.id})`);
}

Step 7: Token Caching (MSAL SerializableTokenCache)

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";

const CACHE_PATH = "./.msal-token-cache.json";

const pca = new PublicClientApplication({
  auth: {
    clientId: process.env.AZURE_CLIENT_ID!,
    authority: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}`,
  },
});

// Load cached tokens on startup
const cache = pca.getTokenCache();
if (fs.existsSync(CACHE_PATH)) {
  cache.deserialize(fs.readFileSync(CACHE_PATH, "utf-8"));
}

// After acquiring a token, persist the cache
const 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_ID
from 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 = new DeviceCodeCredential({ clientId, tenantId });

Resources

Next Steps

  • Run onenote-hello-world to create your first notebook, section, and page
  • See onenote-common-errors for a complete error decoder reference
  • See onenote-sdk-patterns for production retry logic and rate limit handling