onenote-install-auth
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".
What this skill does
# 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](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps)
- 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](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps)
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:**
```bash
pip install msgraph-sdk azure-identity
```
**TypeScript/Node:**
```bash
npm install @microsoft/microsoft-graph-client @azure/identity @azure/msal-node
```
### Step 4: Configure Environment Variables
```bash
# .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)
```python
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)
```typescript
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:
```typescript
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):**
```python
# Paste into Python REPL after setting AZURE_CLIENT_ID and AZURE_TENANT_ID
from azure.identity import DeviceCodeCredential; from msgraph import GraphServiceClient
c = GraphRelated in Ads & Marketing
ads
IncludedMulti-platform paid advertising audit and optimization skill. Analyzes Google, Meta, YouTube, LinkedIn, TikTok, Microsoft, and Apple Ads. 250+ checks with scoring, parallel agents, industry templates, and AI creative generation.
banana
IncludedAI image generation Creative Director powered by Google Gemini Nano Banana models. Use this skill for ANY request involving image creation, editing, visual asset production, or creative direction. Triggers on: generate an image, create a photo, edit this picture, design a logo, make a banner, visual for my anything, and all /banana commands. Handles text-to-image, image editing, multi-turn creative sessions, batch workflows, and brand presets.
rpg-migration-analyzer
IncludedAnalyzes legacy RPG (Report Program Generator) programs from AS/400 and IBM i systems for migration to modern Java applications. Extracts business logic from RPG III/IV/ILE source code, identifies data structures (D-specs), file operations (F-specs), program dependencies (CALLB/CALLP), and converts RPG constructs to Java equivalents. Generates migration reports, complexity estimates, and Java implementation strategies with POJO classes, JPA entities, and service methods. Use when modernizing AS/400 or IBM i legacy systems, analyzing RPG source files (.rpg, .rpgle, .RPGLE), converting RPG to Java, mapping data specifications to Java classes, planning legacy system migration, or when user mentions RPG analysis, Report Program Generator, RPG III/IV/ILE, AS/400 modernization, IBM i migration, packed decimal conversion, or mainframe application rewrite.
brand-library-architect
IncludedBuild a complete brand library for a product — visual asset render pipeline, brand documentation set (BRAND, COPY, MANIFESTO, BIOS, FAQ, GLOSSARY, TONE, PRICING), open-source convention files (README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT), and a self-contained press kit. This skill should be used when the user asks to "build a brand library / brand kit / press kit / brand assets" for a product, "set up a brand library workflow," "create a positioning manifesto plus visual identity," or any combination of brand documentation + visual asset pipeline. Apply phase-by-phase or run end-to-end. Templates are product-agnostic and use {{TOKEN}} placeholders the skill prompts the user to fill.
writing-tech-post
IncludedAuthors engineering blog posts end-to-end: launch deep-dives, incident postmortems, architecture migrations, performance case studies, tutorials, AI/agent system writeups, security disclosures, and research-to-product translations. Picks the correct archetype, plans the abstraction ladder, enforces an evidence cadence (diagrams, benchmarks, profiles, traces, code, ablations), tunes voice against publisher house styles (Datadog, Vercel, GitHub, AWS, Meta, Cloudflare, Jane Street), and runs a pre-publish gate for narrative momentum and disclosure ethics. Use when drafting a new engineering post, restructuring a draft that feels flat, deciding which evidence form belongs where, validating that depth and product context are balanced, or preparing a postmortem, migration, or performance narrative for external publication. Do not use for API reference documentation, README authoring, marketing copy, release notes, generic SEO content, ghost-written executive thought leadership, or non-engineering long-form essays.
blog-google
IncludedGoogle API integration for blog performance: PageSpeed Insights, CrUX Core Web Vitals with 25-week history, Search Console performance, URL Inspection, Indexing API, GA4 organic traffic, NLP entity analysis for E-E-A-T, YouTube video search for embedding, and Google Ads Keyword Planner. Progressive feature availability based on credential tier (API key, OAuth/service account, GA4, Ads). Shares config with claude-seo at ~/.config/claude-seo/google-api.json. Use when user says "google data", "page speed", "core web vitals", "search console", "indexation", "GA4", "keyword research", "nlp entities", "blog performance", "youtube search", "google api setup".