Skip to content
AI & Agents
Skill

/auth

Authenticate with AEM Edge Delivery Services. Opens browser for login and captures token. Works for admin.hlx.page and Config Service APIs regardless of content source (Document Authoring, SharePoint, or Google Drive).

From plugin
adobe-skills
162160 skills6 agents4 MCP
Install
$ npx -y skills add adobe/skills --skill auth --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/auth

Context preview

The summary Claude sees to decide when to auto-load this skill.

Authenticate with AEM Edge Delivery Services. Opens browser for login and captures token. Works for admin.hlx.page and Config Service APIs regardless of content source (Document Authoring, SharePoint, or Google Drive).

SKILL.md

auth.SKILL.md
name: auth
description: Authenticate with AEM Edge Delivery Services. Opens browser for login and captures token. Works for admin.hlx.page and Config Service APIs regardless of content source (Document Authoring, SharePoint, or Google Drive).
license: Apache-2.0
allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
metadata:
  version: "2.0.0"

AEM Edge Delivery Services Authentication

Authenticate to obtain a token for all Edge Delivery Services admin operations. Auto-detects identity provider from org+site — no content source question needed. Opens the user's default browser for login and receives the token via a local callback server.

Token Usage

The `authToken` works for admin APIs:

| API | Header | Usage | |-----|--------|-------| | `admin.hlx.page` | `x-auth-token: ${AUTH_TOKEN}` | Preview, publish, status, code sync, jobs, logs, config | | Config Service | `x-auth-token: ${AUTH_TOKEN}` | Sites, config, secrets, API keys, profiles |

> **Note:** `admin.da.live` uses a separate Adobe IMS token with `Authorization: Bearer` header. See the DA-specific login flow in `ops/resources/da.md`.

When to Use This Skill

  • API returns 401 Unauthorized
  • User says "login", "authenticate", "auth"
  • Before any admin operation when token is missing/expired
  • Before generating guides that need API access

Prerequisites

  • Node.js installed

---

Authentication Flow

Step 1: Check Existing Token

Tokens are cached at the **user level** (`~/.aem/ims-token.json`), shared across all projects.

mkdir -p "${HOME}/.aem"

AUTH_TOKEN=$(node -e "
  const fs = require('fs');
  try {
    const t = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ims-token.json', 'utf8'));
    if (t.authToken && t.authTokenExpiry > Math.floor(Date.now()/1000) + 60) {
      process.stdout.write(t.authToken);
    }
  } catch (e) {}
")

if [ -n "$AUTH_TOKEN" ]; then
  echo "Token valid"
  exit 0
fi

echo "Token missing or expired. Starting login..."

Step 2: Resolve Org and Site

The auto-login endpoint `/login/{org}/{site}/main` redirects to the correct identity provider automatically — no need to know the content source.

**Resolve org and site from available sources (project-config, ops-config, git remote):**

# Try project-config first (handover context)
ORG=$(cat .claude-plugin/project-config.json 2>/dev/null | node -e "
  const d = require('fs').readFileSync(0,'utf8');
  try { process.stdout.write(JSON.parse(d).org || ''); } catch(e) {}
")

# Fallback to ops-config (ops context)
if [ -z "$ORG" ]; then
  ORG=$(node -e "
    const fs = require('fs');
    try {
      const c = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ops-config.json', 'utf8'));
      process.stdout.write(c.org || '');
    } catch(e) {}
  ")
fi

# Site: try git remote first, then ops-config
SITE=$(basename -s .git $(git remote get-url origin 2>/dev/null) 2>/dev/null)
if [ -z "$SITE" ]; then
  SITE=$(node -e "
    const fs = require('fs');
    try {
      const c = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ops-config.json', 'utf8'));
      process.stdout.write(c.site || '');
    } catch(e) {}
  ")
fi

echo "org=${ORG:-NOT SET} site=${SITE:-NOT SET}"

**If `ORG` is empty**, ask the user:

> "I need your organization name to authenticate. You can provide either: > - The org name (the `{org}` in `https://main--site--{org}.aem.page`) > - A preview/live URL like `https://main--mysite--myorg.aem.page/`"

**If user provides a URL**, parse org and site from it:

URL="$USER_INPUT"
if echo "$URL" | grep -q '\.aem\.page\|\.aem\.live'; then
  HOST_PART=$(echo "$URL" | cut -d'/' -f3 | cut -d'.' -f1)
  ORG=$(echo "$HOST_PART" | awk -F'--' '{print $NF}')
  SITE=$(echo "$HOST_PART" | awk -F'--' '{print $(NF-1)}')
  echo "Parsed from URL: org=$ORG site=$SITE"
fi

**If `SITE` is still empty** (not in a git repo and no URL provided), ask the user:

> "I also need a site name to auto-detect your login provider. What is your site name? (the `{site}` part of `https://main--{site}--{org}.aem.page`)"

**Do NOT proceed until both org and site are available.**

Step 3: Capture Token via Loopback Redirect

Opens the user's default browser for login. A temporary local HTTP server receives the token callback after login completes. The user must click "Send" on the confirmation page to deliver the token. Works with all identity providers (Adobe IMS, Google, Microsoft).

**User-facing message (display BEFORE running the script below):**

> **Browser opened for login to `{org}/{site}`. Click "Send" after authenticating — you can close the tab once done.**

Use bold/highlighted formatting so the instruction stands out clearly.

mkdir -p "${HOME}/.aem"

node -e "
const http = require('http');
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');

const TOKEN_PATH = path.join(process.env.HOME, '.aem', 'ims-token.json');
const ORG = '${ORG}';
const SITE = '${SITE}';
const STATE = crypto.randomUUID();

const server = http.createServer((req, res) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

  if (req.method === 'OPTIONS') {
    res.writeHead(204);
    res.end();
    return;
  }

  if (req.method === 'POST') {
    let body = '';
    req.on('data', (chunk) => { body += chunk; });
    req.on('end', () => {
      try {
        const parsed = JSON.parse(body);
        if (parsed.state !== STATE) {
          console.error('State mismatch — ignoring callback');
          res.writeHead(403);
          res.end('State mismatch');
          return;
        }
        const authToken = parsed.authToken;
        if (authToken) {
          const expiresAt = Math.floor(Date.now() / 1000) + 86400;
          fs.writeFileSync(TOKEN_PATH, JSON.stringify({
            authToken,
Read more
Ships withadobe-skills

Repository of Adobe skills for AI coding agents.

Get the whole plugin

Other skills on adobe-skills.