diff --git a/.github/linters/.markdownlint.yml b/.github/linters/.markdownlint.yml deleted file mode 100644 index 7c89f2e2..00000000 --- a/.github/linters/.markdownlint.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -########################### -########################### -## Markdown Linter rules ## -########################### -########################### - -# Linter rules doc: -# - https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md -# -# Note: -# To comment out a single error: -# -# any violations you want -# -# - -# Default state for all rules -default: false - -################# -# Rules by tags # -################# -blanks-around-fences: true # Fenced code blocks should be surrounded by blank lines -blanks-around-headings: true # Headings should be surrounded by blank lines -blanks-around-lists: true # Lists should be surrounded by blank lines -code-block-style: - style: "fenced" -code-fence-style: - style: "backtick" -emphasis-style: - style: "consistent" -fenced-code-language: true # Fenced code blocks should have a language specified -heading-start-left: true # Headings must start at the beginning of the line -heading-style: - style: "atx" -hr-style: true # Horizontal rule style -list-indent: true # Inconsistent indentation for list items at the same level -no-empty-links: true -no-missing-space-atx: true # No space after hash on atx style heading -no-multiple-blanks: true # Multiple consecutive blank lines -no-reversed-links: true -no-space-in-code: true -no-space-in-emphasis: true -no-space-in-links: true -no-trailing-spaces: true -single-trailing-newline: true # Files should end with a single newline character -strong-style: - style: "consistent" -ul-style: - style: "consistent" diff --git a/.github/linters/.yaml-lint.yml b/.github/linters/.yaml-lint.yml deleted file mode 100644 index b10948eb..00000000 --- a/.github/linters/.yaml-lint.yml +++ /dev/null @@ -1,59 +0,0 @@ ---- -########################################### -# These are the rules used for # -# linting all the yaml files in the stack # -# NOTE: # -# You can disable line with: # -# # yamllint disable-line # -########################################### -rules: - braces: - level: warning - min-spaces-inside: 0 - max-spaces-inside: 0 - min-spaces-inside-empty: 1 - max-spaces-inside-empty: 5 - brackets: - level: warning - min-spaces-inside: 0 - max-spaces-inside: 0 - min-spaces-inside-empty: 1 - max-spaces-inside-empty: 5 - colons: - level: warning - max-spaces-before: 0 - max-spaces-after: 1 - commas: - level: warning - max-spaces-before: 0 - min-spaces-after: 1 - max-spaces-after: 1 - comments: disable - comments-indentation: disable - document-end: disable - document-start: - level: warning - present: true - empty-lines: - level: warning - max: 2 - max-start: 0 - max-end: 0 - hyphens: - level: warning - max-spaces-after: 1 - indentation: - level: warning - spaces: consistent - indent-sequences: true - check-multi-line-strings: false - key-duplicates: enable - line-length: - level: warning - max: 100 - allow-non-breakable-words: true - allow-non-breakable-inline-mappings: true - new-line-at-end-of-file: disable - new-lines: - type: unix - trailing-spaces: disable diff --git a/.github/scripts/process-mds.sh b/.github/scripts/process-mds.sh deleted file mode 100755 index 0ceb4cb1..00000000 --- a/.github/scripts/process-mds.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash - -fail() { - echo "$@" 1>&2 - exit 1 -} - -ROOT="./src/pages" -OPERATION=$1 -ENV=$2 -CONTENT_REPO_BRANCH=$3 -PATH_PREFIX=$4 - -# conditional http_method -case "$OPERATION" in - cache | preview | live) - http_method="POST" - ;; - *) - fail "Unknown operation" - ;; -esac - -# conditional site and code_repo_branch -case "$ENV" in - stage) - site="adp-devsite-stage" - code_repo_branch="stage" - ;; - prod) - site="adp-devsite" - code_repo_branch="main" - ;; - *) - fail "Unknown env" - ;; -esac - -# conditional args -if [ "$ENV" == "stage" ] && [ "$OPERATION" == "preview" ] -then - args="--header \"x-content-source-authorization: ${CONTENT_REPO_BRANCH}\"" -else - args="" -fi - -process() -{ - filename=$1 - path="${PATH_PREFIX:1}/${filename#$ROOT/}" - url="https://admin.hlx.page/${OPERATION}/adobedocs/${site}/${code_repo_branch}/${path}" - cmd="curl -X${http_method} -vi ${args} ${url}" - - echo "" - echo "" - echo "--------------------------------------------------------------------------------" - echo "" - echo "${cmd}" - echo "" - - # run command and extract failure string - failure=$(eval "${cmd} | grep -e \"x-error:\"") - - # append to failures - if [ "$failure" != "" ] - then - failures="${failures}\n${cmd}\n${failure}\n" - fi - - # write failures to stderr so it can be accessed outside this subshell later - echo $failures > 2 -} - -summarize() { - echo "" - echo "" - echo "================================================================================" - echo "" - - # read failures from stderr to access it from this parent shell - read -r failures < 2 - - if [ "${failures}" == "" ] - then - echo "Success!" - else - echo "Failures:" - echo -e "${failures}" - fi - - echo "" -} - -# process mds in root -# TODO: may want to only process certain types of files -find "${ROOT}" -type f \( -name "*.md" -o -name "*.json" \) -exec echo "{}" \; | while read i; do process $i; done - -summarize diff --git a/.github/super-linter.env b/.github/super-linter.env deleted file mode 100644 index feebdd46..00000000 --- a/.github/super-linter.env +++ /dev/null @@ -1,6 +0,0 @@ -IGNORE_GITIGNORED_FILES=true -VALIDATE_GITLEAKS=true -VALIDATE_MARKDOWN=true -MARKDOWN_CONFIG_FILE=.markdownlint.yml -VALIDATE_YAML=true -VALIDATE_JSON=true diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2c9d59e3..76dee46d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -12,129 +12,28 @@ on: - stage - prod - stage & prod - clean: - description: "Clean cache?" + baseSha: + description: "Use base SHA commit to deploy from (empty string defaults to last commit before HEAD)" + type: string + required: false + default: "" + deployAll: + description: "Force deploy all files" type: boolean - required: true - default: "no" - build-navigation: - description: "Build navigation file?" - type: boolean - required: true - default: "false" - build-redirections: - description: "Build redirections file?" - type: boolean - required: true - default: "false" - + default: false jobs: - set-state: - runs-on: ubuntu-latest - outputs: - path_prefix: ${{ steps.get_path_prefix.outputs.path_prefix }} - branch_short_ref: ${{ steps.get_branch.outputs.branch }} - deploy_stage: ${{ contains(inputs.env, 'stage') }} - deploy_prod: ${{ contains(inputs.env, 'prod') }} - clean_cache: ${{ inputs.clean }} - build_navigation: ${{ inputs.build-navigation }} - build_redirections: ${{ inputs.build-redirections }} - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Get path prefix - uses: actions/github-script@v7 - id: get_path_prefix - with: - script: | - const script = require('./.github/scripts/get-path-prefix.js'); - script({ core, isStage:"${{ contains(inputs.env, 'stage') }}", isProd:"${{ contains(inputs.env, 'prod') }}" }); - result-encoding: string - - - name: Get branch name - shell: bash - run: echo "branch=${GITHUB_REF#refs/heads/}" >> "$GITHUB_OUTPUT" - id: get_branch - - echo-state: - needs: [set-state] - runs-on: ubuntu-latest - steps: - - run: echo "Deploy to stage - ${{ needs.set-state.outputs.deploy_stage }}" - - run: echo "Deploy to prod - ${{ needs.set-state.outputs.deploy_prod }}" - - run: echo "Clean cache - ${{ needs.set-state.outputs.clean_cache }}" - - run: echo "Build navigation file - ${{ needs.set-state.outputs.build_navigation }}" - - run: echo "Build redirections file - ${{ needs.set-state.outputs.build_redirections }}" - - run: echo "Path prefix - ${{ needs.set-state.outputs.path_prefix }}" - - run: echo "Repository org - ${{ github.event.repository.owner.login }}" - - run: echo "Repository name - ${{ github.event.repository.name }}" - - run: echo "Repository branch - ${{ needs.set-state.outputs.branch_short_ref }}" - - build: - defaults: - run: - shell: bash - needs: [set-state] + deployment: + name: Deployment + uses: AdobeDocs/adp-devsite-workflow/.github/workflows/deploy.yml@main + with: + env: ${{ inputs.env }} + baseSha: ${{ inputs.baseSha }} + deployAll: ${{ inputs.deployAll }} + lint: + name: Lint runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - - - name: Setup Node v18 for Yarn v3 - if: needs.set-state.outputs.build_navigation == 'true' || needs.set-state.outputs.build_redirections == 'true' - uses: actions/setup-node@v4 - with: - node-version: "18" - - - name: Enable Corepack for Yarn v3 - if: needs.set-state.outputs.build_navigation == 'true' || needs.set-state.outputs.build_redirections == 'true' - run: corepack enable - - - name: Install Yarn v4 - if: needs.set-state.outputs.build_navigation == 'true' || needs.set-state.outputs.build_redirections == 'true' - uses: borales/actions-yarn@v4 - with: - cmd: set version stable - - - name: Install Dependencies - if: needs.set-state.outputs.build_navigation == 'true' || needs.set-state.outputs.build_redirections == 'true' - uses: borales/actions-yarn@v3 - env: - YARN_ENABLE_IMMUTABLE_INSTALLS: false - with: - cmd: install - - - name: Build navigation file - if: needs.set-state.outputs.build_navigation == 'true' - uses: borales/actions-yarn@v3 - with: - cmd: buildNavigation - - - name: Build redirections file - if: needs.set-state.outputs.build_redirections == 'true' - uses: borales/actions-yarn@v3 - with: - cmd: buildRedirections - - - name: Clean cache on stage - if: needs.set-state.outputs.clean_cache == 'true' && needs.set-state.outputs.deploy_stage == 'true' - run: | - bash .github/scripts/process-mds.sh cache stage ${{ needs.set-state.outputs.branch_short_ref }} "${{ needs.set-state.outputs.path_prefix }}" - - - name: Clean cache on prod - if: needs.set-state.outputs.clean_cache == 'true' && needs.set-state.outputs.deploy_prod == 'true' - run: | - bash .github/scripts/process-mds.sh cache prod ${{ needs.set-state.outputs.branch_short_ref }} "${{ needs.set-state.outputs.path_prefix }}" - - - name: Deploy to stage - if: needs.set-state.outputs.deploy_stage == 'true' - run: | - bash .github/scripts/process-mds.sh preview stage ${{ needs.set-state.outputs.branch_short_ref }} "${{ needs.set-state.outputs.path_prefix }}" - - - name: Deploy to prod - if: needs.set-state.outputs.deploy_prod == 'true' - run: | - bash .github/scripts/process-mds.sh preview prod ${{ needs.set-state.outputs.branch_short_ref }} "${{ needs.set-state.outputs.path_prefix }}" - bash .github/scripts/process-mds.sh live prod ${{ needs.set-state.outputs.branch_short_ref }} "${{ needs.set-state.outputs.path_prefix }}" + - name: Lint + run: npx --yes github:AdobeDocs/adp-devsite-utils runLint -v diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml deleted file mode 100644 index 50521f85..00000000 --- a/.github/workflows/github-pages.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: Github Pages -on: - push: - branches: [main] -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Yarn Install - uses: bahmutov/npm-install@v1 - - name: Build - run: | - yarn build - env: - PREFIX_PATHS: true # works like --prefix-paths flag for 'gatsby build' - PATH_PREFIX: ${{ github.event.repository.name }} - ADOBE_LAUNCH_SRC: ${{ secrets.AIO_ADOBE_LAUNCH_SRC }} - ADOBE_LAUNCH_SRC_INCLUDE_IN_DEVELOPMENT: ${{ secrets.ADOBE_LAUNCH_SRC_INCLUDE_IN_DEVELOPMENT }} - REPO_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPO_OWNER: ${{ github.event.repository.owner.login }} - REPO_NAME: ${{ github.event.repository.name }} - REPO_BRANCH: ${{ github.ref_name }} - GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.GOOGLE_OAUTH_CLIENT_ID }} - GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.GOOGLE_OAUTH_CLIENT_SECRET }} - GOOGLE_DOCS_TOKEN: ${{ secrets.GOOGLE_DOCS_TOKEN }} - GOOGLE_DOCS_FOLDER_ID: ${{ secrets.GOOGLE_DOCS_FOLDER_ID }} - - name: Deploy to GH Pages - uses: JamesIves/github-pages-deploy-action@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: gh-pages # The branch the action should deploy to. - folder: public # The folder the action should deploy. - clean: true # Automatically remove deleted files from deploy branch - - name: GH Pages URL - id: gh-pages-url - run: | - echo "View GH-Pages: $(https://adobedocs.github.io/${{ github.event.repository.name }})" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..18f1e49d --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,17 @@ +--- + name: Lint + on: + pull_request: + branches: [main] + paths: + - 'src/pages/**' + + jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Lint + run: npx --yes github:AdobeDocs/adp-devsite-utils runLint -v \ No newline at end of file diff --git a/.github/workflows/resuse-deploy.yml b/.github/workflows/resuse-deploy.yml new file mode 100644 index 00000000..1bee8dc2 --- /dev/null +++ b/.github/workflows/resuse-deploy.yml @@ -0,0 +1,26 @@ +--- +name: Reuse Deployment +on: + workflow_dispatch: + inputs: + env: + description: "Select environment to deploy to" + type: choice + required: true + default: "stage" + options: + - stage + - prod + baseSha: + description: "Use base SHA commit to deploy from (empty string defaults to last commit before HEAD)" + type: string + required: false + default: "" + +jobs: + reuse_deployment: + name: Reuse Deployment + uses: AdobeDocs/adp-devsite-workflow/.github/workflows/deploy.yml@main + with: + env: ${{ inputs.env }} + baseSha: ${{ inputs.baseSha }} diff --git a/.github/workflows/test-pull-request.yml b/.github/workflows/test-pull-request.yml deleted file mode 100644 index cb4cc8f6..00000000 --- a/.github/workflows/test-pull-request.yml +++ /dev/null @@ -1,92 +0,0 @@ ---- -########################### -########################### -## Pull request testing ## -########################### -########################### -name: Latest Pull Request - -# Documentation: -# - Workflow: https://help.github.com/en/articles/workflow-syntax-for-github-actions -# - SuperLinter: https://github.com/github/super-linter -# - Markdown linter: https://github.com/DavidAnson/markdownlint -# - Link validation: https://github.com/remarkjs/remark-validate-links - -###################################################### -# Start the job on a pull request to the main branch # -###################################################### -on: - pull_request: - branches: [main] - -############### -# Set the Job # -############### -jobs: - validate: - # Set the agent to run on - runs-on: ubuntu-latest - - ################## - # Load all steps # - ################## - steps: - ########################## - # Checkout the code base # - ########################## - - name: Checkout Code - uses: actions/checkout@v3 - with: - # Full git history is needed to get a proper list of changed files - # within `super-linter` - fetch-depth: 0 - - ################################ - # Run Linters against code base # - ################################ - - name: Lint Code Base - # - # Use full version number to avoid cases when a next - # released version is buggy - # About slim image: https://github.com/github/super-linter#slim-image - uses: github/super-linter/slim@v4.9.4 - env: - VALIDATE_ALL_CODEBASE: false - DEFAULT_BRANCH: main - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VALIDATE_GITHUB_ACTIONS: true - VALIDATE_GITLEAKS: true - # - # The Markdown rules are defined at - # .github/linters/.markdown-lint.yml - # - # Documentation on rules: - # https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md - VALIDATE_MARKDOWN: true - VALIDATE_YAML: true - - - name: Setup Node v16 for Yarn v3 - uses: actions/setup-node@v3 - with: - node-version: '16.15.0' # Current LTS version - - - name: Enable Corepack for Yarn v3 - run: corepack enable - - - name: Install Yarn v3 - uses: borales/actions-yarn@v3 - with: - cmd: set version stable - - - name: Install dependencies - uses: borales/actions-yarn@v3 - env: - YARN_ENABLE_IMMUTABLE_INSTALLS: false - with: - cmd: install - - - name: Build site - if: ${{ success() }} - uses: borales/actions-yarn@v3 - with: - cmd: build diff --git a/README.md b/README.md index 08a63346..978030fb 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,3 @@ -# Adobe I/O Documentation Template +# Documentation -This is a site template built with the [Adobe I/O Theme](https://github.com/adobe/aio-theme). - -View the [demo](https://adobedocs.github.io/dev-site-documentation-template/) running on Github Pages. - -## Where to ask for help - -The slack channel #adobeio-onsite-onboarding is our main point of contact for help. Feel free to join the channel and ask any questions. - -## How to develop - -For local development, simply use : -``` -$ npm install -$ npm run dev -``` - -For the documentation developer, please read these sections on how to: -- [Arrange the structure content of your docs](https://github.com/adobe/aio-theme#content-structure) -- [Linking to pages](https://github.com/adobe/aio-theme#links) -- [Using assets](https://github.com/adobe/aio-theme-aio#assets) -- [Setting Global Navigation](https://github.com/adobe/aio-theme#global-navigation) -- [Setting Side Navigation](https://github.com/adobe/aio-theme#side-navigation) -- [Using content blocks](https://github.com/adobe/aio-theme#jsx-blocks) -- [Notes on using Markdown](https://github.com/adobe/aio-theme#writing-enhanced-markdown) - -For more in-depth [instructions](https://github.com/adobe/aio-theme#getting-started). - -## How to deploy - -For any team that wishes to deploy to the adobe.io and stage.adobe.io website, they must be in contact with the dev-site team. Teams will be given a path that will follow the pattern `adobe.io/{product}/`. This will allow doc developers to setup their subpaths to look something like: -``` -adobe.io/{product}/docs -adobe.io/{product}/community -adobe.io/{product}/community/code_of_conduct -adobe.io/{product}/community/contribute -``` - -### Launching a deploy - -You can deploy using the GitHub actions deploy workflow see [deploy instructions](https://github.com/adobe/aio-theme#deploy-to-azure-storage-static-websites). \ No newline at end of file +Please see the [centralized README](https://github.com/AdobeDocs/adp-devsite-utils/blob/main/README.md). diff --git a/buildNavigation.js b/buildNavigation.js deleted file mode 100644 index 8b133c44..00000000 --- a/buildNavigation.js +++ /dev/null @@ -1,106 +0,0 @@ -const path = require('path'); -const fs = require('node:fs'); -// regex to find sections: -// subPages:((\s* .*)*) - -const { siteMetadata, pathPrefix } = require('./gatsby-config.js'); - -try { - if(!pathPrefix) { - throw new TypeError("pathPrefix not found"); - } - - let topNavMarkdown = ``; - // TODO: prob need url fixer from gatsby theme - // home link defines the first link defaults to Products - // can be hidden - // siteMetadata.versions - // siteMetadata.home - - topNavMarkdown += `- pathPrefix:\n`; - topNavMarkdown += ` - ${pathPrefix}\n`; - - if (siteMetadata.home) { - topNavMarkdown += '\n- home:\n'; - topNavMarkdown += ` - [${siteMetadata.home.title}](${siteMetadata.home.path})\n`; - - if(siteMetadata.home.hidden) { - topNavMarkdown += ` - hidden\n`; - } - } - - if (siteMetadata.versions) { - topNavMarkdown += '\n- versions:\n'; - - siteMetadata.versions.forEach((versionItem) => { - let isSelectedText = versionItem.selected ? `selected` : ''; - let versionPathText = versionItem.path ? versionItem.path : '/'; - topNavMarkdown += ` - [${versionItem.title}](${versionPathText}) ${isSelectedText}\n`; - }); - } - - if(siteMetadata.pages) { - topNavMarkdown += `\n- pages:\n`; - } - - siteMetadata.pages?.forEach((navItem) => { - //let pathText = navItem.path ? navItem.path : ''; - if(navItem.path) { - topNavMarkdown += ` - [${navItem.title}](${navItem.path})\n`; - } else { - topNavMarkdown += ` - ${navItem.title}\n`; - navItem.menu.forEach((menuItem) =>{ - topNavMarkdown += ` - [${menuItem.title}](${menuItem.path})\n`; - }); - } - }); - - if(siteMetadata.subPages) { - topNavMarkdown += `\n- subPages:\n`; - let sideNavMarkdown = ``; - let depth = 1; - - sideNavMarkdown += buildSideNavRecursively(siteMetadata.subPages, depth); - topNavMarkdown += sideNavMarkdown; - } - - let configFilePath = path.resolve(__dirname + '/src/pages/config.md'); - fs.writeFileSync(configFilePath, topNavMarkdown); - console.log(`Generated file: ${configFilePath}`); - -} catch (err) { - console.error(err); -} -// subpages menu should only appear on the subpages path -// need to check paths to -function buildSideNavRecursively(sideNav, depth) { - let sideNavMarkdown = ''; - - for (var k in sideNav) { - let header = sideNav[k].header ? 'header' : ''; - sideNavMarkdown += `${insertSpace(depth)}- [${sideNav[k].title}](${sideNav[k].path}) ${header}\n`; - - if (sideNav[k].pages) { - sideNavMarkdown += buildSideNavRecursively(sideNav[k].pages, depth+1); - } - } - return sideNavMarkdown; -} - -function insertSpace(indentLevel) { - let spaces = ``; - for(var i=0; i { - mdFilePath = mdFilePath.replace(__dirname + '/src/pages', pathPrefix); - mdFilePath = path.resolve(mdFilePath); - - // Fixes paths that don't end in a trailing slash but should. - // index.md is a directory-level URL that needs a trailing slash - if(mdFilePath.endsWith('index.md')) { - const source = mdFilePath.replace('/index.md', ''); - data.push({ - "Source" : source, - "Destination" : source + '/' - }); - data.push({ - "Source" : source + '/index', - "Destination" : source + '/' - }); - } - // Fixes paths that end in a trailing slash but shouldn't. - // skip any index.md or config.md as they don't need redirect - else if(!mdFilePath.endsWith('config.md')) { - const source = mdFilePath.replace('.md', '/'); - data.push({ - "Source" : source, - "Destination" : source.replace(/\/$/, "") - }); - } - }); - - let redirectionsData = - { - "total" : data.length, - "offset": 0, - "limit": data.length, - "data" : data, - ":type": "sheet" - }; - - let redirectionsFilePath = path.resolve(__dirname + '/src/pages/redirects.json'); - fs.writeFileSync(redirectionsFilePath, JSON.stringify(redirectionsData)); - -} catch (err) { - console.error(err); -} diff --git a/dev.mjs b/dev.mjs new file mode 100644 index 00000000..412e84de --- /dev/null +++ b/dev.mjs @@ -0,0 +1,28 @@ +// content/docs +// serve static on 3001 + +import express from 'express'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const PORT = process.env.DEV_PORT || 3003; + +// TODO: ensure `DOCS_DIRECTORY` starts with `/` +const DOCS_DIRECTORY = process.env.DIRECTORY || './src/pages'; + +const app = express(); +console.log(path.resolve(__dirname, `./${DOCS_DIRECTORY}`)); +app.use( + express.static(path.resolve(__dirname, `./${DOCS_DIRECTORY}`), { + setHeaders: (res) => { + res.setHeader('last-modified', new Date().toGMTString()); + }, + }), +); + +app.listen(PORT, () => { + console.debug(`Docs dev server is running on port ${PORT}`); +}); \ No newline at end of file diff --git a/gatsby-config.js b/gatsby-config.js deleted file mode 100644 index 7588f5e8..00000000 --- a/gatsby-config.js +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2020 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ - -module.exports = { - siteMetadata: { - pages: [ - { - path: "/developer-console", - title: "Developer Console" - }, - { - path: "guides/authentication/index.md", - title: "Authentication Guide" - }, - { - path: "guides/index.md", - title: "Documentation" - }, - { - path: "support/index.md", - title: "Support" - } - ], - subPages: [ - { - path: "guides/index.md", - title: "Developer Console", - pages: [ - { - path: "guides/getting-started.md", - title: "Getting Started" - }, - { - path: "guides/projects/index.md", - title: "Projects", - pages: [ - { - path: "guides/projects/projects-empty.md", - title: "Create an Empty Project" - }, - { - path: "guides/projects/projects-template.md", - title: "Create a Templated Project" - }, - { - path: "guides/projects/approval.md", - title: "Projects Approval" - }, - { - path: "guides/projects/beta-users.md", - title: "Beta Users" - } - ] - }, - { - path: "guides/plugins/index.md", - title: "Plugins", - pages: [ - { - path: "guides/plugins/plugin-distribution.md", - title: "Distribute a Plugin" - }, - { - path: "guides/plugins/plugin-update.md", - title: "Update a Plugin" - } - ] - }, - { - path: "guides/services/index.md", - title: "Services", - pages: [ - { - path: "guides/services/services-add-api-oauth-s2s.md", - title: "Add API using OAuth Server-to-Server credential" - }, - { - path: "guides/services/services-add-api-key.md", - title: "Add API using API Key credential" - }, - { - path: "guides/services/services-add-api-oauth-user-authentication.md", - title: "Add API using OAuth User authentication credential" - }, - { - path: "guides/services/services-add-event.md", - title: "Add Events" - }, - { - path: "guides/services/services-enable-runtime.md", - title: "Enable Runtime" - } - ] - }, - { - path: "guides/email-alerts/index.md", - title: "Email Alerts", - pages: [ - { - path: "guides/email-alerts/cert-expiry.md", - title: "Certificate Expiry" - } - ] - }, - { - path: "guides/credentials.md", - title: "Credentials" - }, - { - path: "guides/insights.md", - title: "Insights" - }, - { - path: "guides/apis-and-services.md", - title: "APIs and services" - }, - { - path: "guides/public-profile.md", - title: "Public Profile" - }, - { - path: "guides/quota.md", - title: "Quota" - } - ] - }, - { - path: "guides/authentication/index.md", - title: "Authentication", - pages: [ - { - pages: [ - { - path: "guides/authentication/UserAuthentication/implementation.md", - title: "Implementation Guide" - }, - { - path: "guides/authentication/UserAuthentication/ims.md", - title: "API Reference" - } - ], - path: "guides/authentication/UserAuthentication/index.md", - title: "User Authentication" - }, - { - pages: [ - { - path: "guides/authentication/ServerToServerAuthentication/implementation.md", - title: "Implementation Guide" - }, - { - path: "guides/authentication/ServerToServerAuthentication/migration.md", - title: "Migration Guide" - }, - { - path: "guides/authentication/ServerToServerAuthentication/faqs.md", - title: "Migration FAQs" - }, - { - path: "guides/authentication/ServerToServerAuthentication/ims.md", - title: "API Reference" - }, - { - pages: [ - { - path: "guides/authentication/JWT/jwt-certificate.md", - title: "Create a Public Key Certificate" - }, - { - path: "guides/authentication/JWT/scopes.md", - title: "JWT Metascopes" - }, - { - path: "guides/authentication/JWT/samples.md", - title: "JWT Sample Code" - } - ], - path: "guides/authentication/JWT/index.md", - title: "Service Account (JWT) credential" - } - ], - path: "guides/authentication/ServerToServerAuthentication/index.md", - title: "Server to Server Authentication" - }, - { - path: "guides/authentication/APIKeyAuthentication/index.md", - title: "API Key Authentication" - }, - { - pages: [ - { - path: "guides/authentication/Tools/o-auth-playground.md", - title: "OAuth 2.0 Playground" - }, - { - path: "guides/authentication/Tools/postman.md", - title: "Postman" - } - ], - path: "guides/authentication/Tools/index.md", - title: "Tools" - } - ] - }, - { - path: "support/index.md", - title: "Support", - pages: [ - { - path: "support/faq.md", - title: "FAQ" - } - ] - } - ] - }, - plugins: [`@adobe/gatsby-theme-aio`], - pathPrefix: process.env.PATH_PREFIX || "/developer-console/docs/" -}; diff --git a/gatsby-ssr.js b/gatsby-ssr.js deleted file mode 100644 index 39c2f059..00000000 --- a/gatsby-ssr.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2021 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ - -import React from 'react'; -import {withPrefix} from 'gatsby'; - -export const onRenderBody = ({setHeadComponents}) => { - setHeadComponents([ - - ]); -}; \ No newline at end of file diff --git a/package.json b/package.json index 127ad901..0a6b6808 100644 --- a/package.json +++ b/package.json @@ -22,28 +22,17 @@ "gatsby-sharp": "1.12.0" }, "scripts": { - "start": "gatsby build && gatsby serve", - "start:prefix": "gatsby build --prefix-paths && gatsby serve --prefix-paths", - "dev": "gatsby develop", - "dev:https": "gatsby develop --https --host localhost.corp.adobe.com --port 9000", - "build": "gatsby build", - "build:incremental": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build --log-pages", - "serve": "gatsby serve", - "clean": "gatsby clean", - "test:links": "remark src/pages --quiet --frail", - "lint": "docker run --rm -e RUN_LOCAL=true --env-file '.github/super-linter.env' -v \"$PWD\":/tmp/lint github/super-linter:slim-v4.10.1", - "buildNavigation": "node buildNavigation.js", - "buildRedirections": "node buildRedirections.js" - }, - "remarkConfig": { - "plugins": [ - "remark-validate-links" - ] + "dev": "node ./dev.mjs", + "buildNavigation": "npx --yes github:AdobeDocs/adp-devsite-utils buildNavigation -v", + "buildRedirections": "npx --yes github:AdobeDocs/adp-devsite-utils buildRedirections -v", + "renameFiles": "npx --yes github:AdobeDocs/adp-devsite-utils renameFiles -v", + "normalizeLinks": "npx --yes github:AdobeDocs/adp-devsite-utils normalizeLinks -v", + "checkLinks": "npx --yes github:AdobeDocs/adp-devsite-utils checkLinks -v", + "buildSiteWideBanner": "npx --yes github:AdobeDocs/adp-devsite-utils buildSiteWideBanner -v", + "lint": "npx --yes github:AdobeDocs/adp-devsite-utils runLint -v" }, "packageManager": "yarn@3.2.2", "devDependencies": { - "glob": "11.0.0", - "remark-cli": "^11.0.0", - "remark-validate-links": "^12.1.0" + "glob": "11.0.0" } } diff --git a/samples/adobe-jwt-dotnet/Program.cs b/samples/adobe-jwt-dotnet/Program.cs deleted file mode 100644 index 7d4f34b8..00000000 --- a/samples/adobe-jwt-dotnet/Program.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Security.Cryptography.X509Certificates; -using Jose; -using RestSharp; -using System.Collections.Generic; - -//NuGet packages: Jose and RestSharp - -namespace adobe_jwt_dotnet -{ - class Program - { - static void Main(string[] args) - { - //Copy these details from console.adobe.io integration - const string CLIENT_ID = "YOUR API KEY (CLIENT ID)"; //e.g. ......328adcb8062d - const string CLIENT_SECRET = "YOUR CLIENT SECRET"; //e.g. ......-ad95-70e50b6b4ea3 - const string TECH_ACC_ID = "YOUR TECHNICAL ACCOUNT ID"; //e.g. ......0A495C2A@techacct.adobe.com - const string ORG_ID = "YOUR ORGANIZATION ID"; //e.g. ......0A495D09@AdobeOrg - const string METASCOPES = "YOUR METASCOPES IN COMMA SEPARATED FORMAT"; //e.g. "https://ims-na1.adobelogin.com/s/ent_reactor_developer_sdk,https://ims-na1.adobelogin.com/s/ent_reactor_admin_sdk" - - - Dictionary test = new Dictionary(); - test.Add("exp", DateTimeOffset.Now.ToUnixTimeSeconds()+600); - test.Add("iss", ORG_ID); - test.Add("sub", TECH_ACC_ID); - test.Add("aud", "https://ims-na1.adobelogin.com/c/"+CLIENT_ID); - string[] scopes = METASCOPES.Split(','); - - foreach(string scope in scopes) - { - test.Add(scope, true); - } - - //You must have generated a certificate using below command and uploaded in the console.adobe.io integration - //openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out certificate_pub.crt - - //Create a pfx file using the private key and public certificate generated from the above step - //openssl pkcs12 -export -out mycert.pfx -inkey private.key -in certificate_pub.crt - //Enter export password: password - - try - { - X509Certificate2 cert = new X509Certificate2("/path/to/mycert.pfx", "password"); - - string token = Jose.JWT.Encode(test, cert.GetRSAPrivateKey(), JwsAlgorithm.RS256); - - Console.WriteLine(token); //Intermediate JWT Token - - var client = new RestClient("https://ims-na1.adobelogin.com/ims/exchange/jwt/"); - - var request = new RestRequest(Method.POST); - request.AddHeader("cache-control", "no-cache"); - request.AddHeader("content-type", "multipart/form-data; boundary=----boundary"); - request.AddParameter("multipart/form-data; boundary=----boundary", - "------boundary\r\nContent-Disposition: form-data; name=\"client_id\"\r\n\r\n" + CLIENT_ID + - "\r\n------boundary\r\nContent-Disposition: form-data; name=\"client_secret\"\r\n\r\n" + CLIENT_SECRET + - "\r\n------boundary\r\nContent-Disposition: form-data; name=\"jwt_token\"\r\n\r\n" + token + - "\r\n------boundary--", ParameterType.RequestBody); - - - IRestResponse response = client.Execute(request); - - Console.WriteLine(response.Content); - } - catch(Exception e) - { - Console.WriteLine("An error has occured: "+e.Message); - } - - - } - } -} \ No newline at end of file diff --git a/samples/adobe-jwt-dotnet/readme.md b/samples/adobe-jwt-dotnet/readme.md deleted file mode 100644 index 4b303732..00000000 --- a/samples/adobe-jwt-dotnet/readme.md +++ /dev/null @@ -1,16 +0,0 @@ - -# Steps - -1. create the certificate and private key using openssl - -```$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out certificate_pub.crt``` - -2. Upload the certificate_pub.crt in Adobe IO Console-> Your Integration-> Public keys - -3. convert private key and certificate to pfx format - -```$ openssl pkcs12 -export -out mycert.pfx -inkey private.key -in certificate_pub.crt``` - -4. Edit the program.cs file and add the properties from your Adobe Developer Console integration. - - diff --git a/samples/adobe-jwt-java/pom.xml b/samples/adobe-jwt-java/pom.xml deleted file mode 100644 index f88d8fcb..00000000 --- a/samples/adobe-jwt-java/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - 4.0.0 - - - 1.7 - 1.7 - - - io.adobe.solutions - IMS-Client - 1.0-SNAPSHOT - - - - - io.jsonwebtoken - jjwt - 0.9.0 - - - - org.json - json - 20171018 - - - - - - \ No newline at end of file diff --git a/samples/adobe-jwt-java/readme.md b/samples/adobe-jwt-java/readme.md deleted file mode 100644 index cfb7d787..00000000 --- a/samples/adobe-jwt-java/readme.md +++ /dev/null @@ -1,16 +0,0 @@ -# Steps - -1. create the certificate and private key using openssl - -```$ openssl req -nodes -text -x509 -newkey rsa:2048 -keyout secret.pem -out certificate.pem -days 356``` - -2. Upload the certificate.pem in Adobe IO Console-> Your Integration-> Public keys - -3. convert private key to DER format - -```$ openssl pkcs8 -topk8 -inform PEM -outform DER -in secret.pem -nocrypt > secret.key``` - -4. Edit the config.properties in src/main/resources/ and add the values from your Adobe Developer Console integration. - - - diff --git a/samples/adobe-jwt-java/secret.key b/samples/adobe-jwt-java/secret.key deleted file mode 100644 index 2e95d6db..00000000 --- a/samples/adobe-jwt-java/secret.key +++ /dev/null @@ -1 +0,0 @@ -< TO BE CREATED BY USER > diff --git a/samples/adobe-jwt-java/src/main/java/io/adobe/solutions/IMSClient.java b/samples/adobe-jwt-java/src/main/java/io/adobe/solutions/IMSClient.java deleted file mode 100644 index ac473530..00000000 --- a/samples/adobe-jwt-java/src/main/java/io/adobe/solutions/IMSClient.java +++ /dev/null @@ -1,156 +0,0 @@ -package io.adobe.solutions; - -import static java.lang.Boolean.TRUE; - -import java.io.BufferedReader; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.interfaces.RSAPrivateKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.KeySpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import javax.net.ssl.HttpsURLConnection; - -import org.json.JSONObject; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; - -public class IMSClient { - public static void main(String[] args) throws IOException { - Properties prop = new Properties(); - - InputStream input = IMSClient.class.getClassLoader().getResourceAsStream("config.properties"); - // load a properties file - prop.load(input); - - try { - String jwtToken = getJWTToken(prop); - if(jwtToken != null && jwtToken != "") { - System.out.println("JWT Token: " + jwtToken); - String accessToken = getAccessToken(prop, jwtToken); - System.out.println("Access Token: " + accessToken); - } - } catch(Exception e) { - System.out.println("Error Occurred: " + e.getMessage()); - } - } - - public static String getJWTToken(Properties prop) - throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { - // Load relevant properties from prop file - String orgId = prop.getProperty("orgId"); - String technicalAccountId = prop.getProperty("technicalAccountId"); - String apiKey = prop.getProperty("apiKey"); - String keyPath = prop.getProperty("key_path"); - String imsHost = prop.getProperty("imsHost"); - // Expiration time in seconds - Long expirationTime = System.currentTimeMillis() / 1000 + 86400L; - // Metascopes associated to key - String metascopes[] = prop.getProperty("metascopes").split(","); - - // # create the certificate and private key using openssl - // $ openssl req -nodes -text -x509 -newkey rsa:2048 -keyout secret.pem -out - // certificate.pem -days 356 - // - // Upload the certificate.pem in Adobe IO Console-> Your Integration-> Public - // keys - // - // # convert private key to DER format - // $ openssl pkcs8 -topk8 -inform PEM -outform DER -in secret.pem -nocrypt > - // secret.key - - // Secret key as byte array. Secret key file should be in DER encoded format. - byte[] privateKeyFileContent = Files.readAllBytes(Paths.get(keyPath)); - - // Read the private key - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - KeySpec ks = new PKCS8EncodedKeySpec(privateKeyFileContent); - RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(ks); - - // Create JWT payload - Map jwtClaims = new HashMap<>(); - jwtClaims.put("iss", orgId); - jwtClaims.put("sub", technicalAccountId); - jwtClaims.put("exp", expirationTime); - jwtClaims.put("aud", "https://" + imsHost + "/c/" + apiKey); - for(String metascope : metascopes) { - jwtClaims.put("https://" + imsHost + "/s/" + metascope, TRUE); - } - - SignatureAlgorithm sa = SignatureAlgorithm.RS256; - // Create the final JWT token - String jwtToken = Jwts.builder().setClaims(jwtClaims).signWith(sa, privateKey).compact(); - - return jwtToken; - } - - public static String getAccessToken(Properties prop, String jwtToken) throws IOException { - // Load relevant properties from prop file - String accessToken = ""; - String imsExchange = prop.getProperty("imsExchange"); - String apiKey = prop.getProperty("apiKey"); - String secret = prop.getProperty("secret"); - - URL obj = new URL(imsExchange); - - HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); - - // add request header - con.setRequestMethod("POST"); - - // Add parameters to request - String urlParameters = "client_id=" + apiKey + "&client_secret=" + secret + "&jwt_token=" + jwtToken; - - // Send post request - con.setDoOutput(true); - DataOutputStream wr = new DataOutputStream(con.getOutputStream()); - wr.writeBytes(urlParameters); - wr.flush(); - wr.close(); - - int responseCode = con.getResponseCode(); - System.out.println("Sending 'POST' request to URL: " + imsExchange); - System.out.println("Post parameters: " + urlParameters); - System.out.println("Response Code: " + responseCode); - - boolean responseError = false; - InputStream is; - if(responseCode < HttpsURLConnection.HTTP_BAD_REQUEST) { - is = con.getInputStream(); - } else { - /* error from server */ - is = con.getErrorStream(); - responseError = true; - } - - BufferedReader in = new BufferedReader(new InputStreamReader(is)); - String inputLine; - StringBuilder response = new StringBuilder(); - - while((inputLine = in.readLine()) != null) { - response.append(inputLine); - } - in.close(); - - if(responseError) { - System.out.println(response.toString()); - } - - JSONObject jObject = new JSONObject(response.toString()); - accessToken = jObject.getString("access_token"); - - return accessToken; - } -} diff --git a/samples/adobe-jwt-java/src/main/resources/config.properties b/samples/adobe-jwt-java/src/main/resources/config.properties deleted file mode 100644 index ba72866a..00000000 --- a/samples/adobe-jwt-java/src/main/resources/config.properties +++ /dev/null @@ -1,27 +0,0 @@ -#Go to https://www.adobe.com/go/devs_console_ui -> Select your integration and copy the client credential from "Overview" -#API Key (Client ID) -apiKey=6cdd92XXXXXXXXb82de19827..... - -#Technical account ID -technicalAccountId=95FXXXXXXXXBA542B9....@techacct.adobe.com - -#Organization ID -orgId=C74FXXXXXX4880XXXXX9@AdobeOrg - -#Client secret -secret=ae2XXXXc-XXXX-XXXX-bf77-b35..... - -#Go to "JWT" section of the integration and copy below values -#exp -expirationTime=600 - -#scopes e.g. ent_user_sdk from "https://ims-na1.adobelogin.com/s/ent_user_sdk" -metascopes=ent_user_sdk - -#Path to secret.key file for the certificate uploaded in console.adobe.io integration -key_path=secret.key - -#URL Endpoints -imsHost=ims-na1.adobelogin.com -imsExchange=https://ims-na1.adobelogin.com/ims/exchange/jwt - diff --git a/samples/adobe-jwt-node/app.js b/samples/adobe-jwt-node/app.js deleted file mode 100644 index e3b4dc71..00000000 --- a/samples/adobe-jwt-node/app.js +++ /dev/null @@ -1,5 +0,0 @@ -const auth=require("@adobe/jwt-auth"); -const _config=require("./config.js"); -let options=_config.credentials; -auth(options).then(res => console.log(res)); - diff --git a/samples/adobe-jwt-node/config.js b/samples/adobe-jwt-node/config.js deleted file mode 100644 index 394e9396..00000000 --- a/samples/adobe-jwt-node/config.js +++ /dev/null @@ -1,38 +0,0 @@ -//console.adobe.io -> Integration -> Details - -module.exports = { - credentials: { - clientId : "bb11ebd6406a4f---------f705b26a", //Application ID (Client ID) - technicalAccountId : "8EDD3561-------5CDB@techacct.adobe.com", //Technical account ID - orgId : "EF2D363--------CA@AdobeOrg", // Org ID - clientSecret : "c811f67e-9ed------------25ea148", //Client Secret - metaScopes : "ent_user_sdk", // Metascopes - privateKey : "-----BEGIN RSA PRIVATE KEY-----\n" + //Content of private.key - "MIIEowIBAAKCAQEAtqX3bHwETnyOizuDUWoyT0PTreyQJ9Hkti+5CssnRULAsE/D\n" + - "Yw6RyB8WLlC07mYxUkA/7Ikd1oEvTuAYLCSuJWXu0WBhXHrw6268xP60LjQl1Xu5\n" + - "GQjrTiQll8c0N10dww6TzzupLyFOD/J+TsH9T2zZ/DYukP/x0iOGs/7D3IvJfRcq\n" + - "6FOoAcKSV3geW7IRDnmn+dX4W3SWN/SS9OGRgYu16KBYV2+YvAkRtWM3nMdLuz6a\n" + - "ep16MBZJQErYguowmhrSmLRFOzkZbXPjr6JZmZaNuuPLQnLYLA2p0RUzva5PThkw\n" + - "esidMH2WZQ5dP+4aDA8mwz6eiI6z4+WR8gp3DwIDAQABAoIBADmMN1YGCTFVi3AR\n" + - "E0IOrMVOwJ5XH+B8AIz------------------------------ZzVw45acX1JfMdk\n" + - "UhlugTQlVsa/kE2cKtZzMKT8/3ZjVdxavCpIJiObvgCBclFTVHJF4Ip16MuBud/K\n" + - "bsOP2akxycjupWNoUR/9s4544etV0smbm2wV/KUVth8VFdRMaE2b0xHeCOvS4MP2\n" + - "j1sSqdSmKUBddooFjQwDC/poOQC0GYj+VXEiHrR/uk/P/ipO2Q7N4+2DIDXAe1d7\n" + - "0A6reBaPagbvH---------------------------oX52CDVFJRQzncCXe6WD9tPT\n" + - "ldx9F6ECgYEA8fMntK4pjLKVGkmjuHCmYZqCQfElbxZWBGgeTi6TtQAC/K0NqivL\n" + - "57SMjiZEdE1Ct716PHs8YCHNVQVdPD2Bpv9HXq9Jcz+nQj0onofqsplnj/0nH3ls\n" + - "fJGo27nB8LYDINWXEY3Mr8H7/Xs5/lQIDdoadWriJHCifO0sSdih/b8CgYEAwUE6\n" + - "/PtHPp315mI33azUwZDZ+G4DwI2WICoGs9Z1RlqH8Zk3X11djpGurNNK5/JcPtSS\n" + - "No4CAEX4VYue+N2jtQnJNzH+IhLFBGDwz1JMBMBw1ye8SVtvvgYyeRnmdmsGP1cj\n" + - "Lb8jQckg/1q6lnJvHLq39P8uhAYmNPU7HTojerECgYA1K1w0ZnmXA7+kcT5bT9Yg\n" + - "aNXrK9UwHlZ0zXSrdazb/86oPFdeaBV9K74fluVAMaJRwn/UPri733HCF1Wv4UxW\n" + - "qI7Ejfy0KO7WrZ35iKDY8sFBcT5huktO7KuQ7bdi8HyrgIScRO8QSK7Zu3S5ITpL\n" + - "4PwjhYOAwofSsEQlTkf6/QKBgAR9spQ8dqxi2VsQP0IciRkyd45JaDbqU2nN8Dfv\n" + - "rOwEoJIhwGKr9cehZRDJHD2FqVUgdYFlDXDaL2o1g20/IVXMs2tf8wrxqrhuVEpN\n" + - "HE0j97tYRgziXhdpZ3TfADcSR6PjL4MZpQUbWnP0tM9YO3LMeAfugjM5PVwPst78\n" + - "AeURAoGBANQ0s6p/ZGV9pmteoN452Oz+PHD/WCc1wid9m2eKawqNYRODC6ls1eQ+\n" + - "h1veQozYJLLob3+fVgKiHo/KEtA/4Svq7/+2eW4rBs12C6N+bSBj01no6nmqrDrO\n" + - "j+fOdGjzGxHSiqI8QvKa+9JJodXzfKNT2ALzV/Q7h6OHjsP549Ze\n" + - "-----END RSA PRIVATE KEY-----\n" - } -} \ No newline at end of file diff --git a/samples/adobe-jwt-node/package.json b/samples/adobe-jwt-node/package.json deleted file mode 100644 index 1c056777..00000000 --- a/samples/adobe-jwt-node/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "adobe-jwt-node", - "version": "1.0.0", - "description": "This is a sample code", - "scripts": { - "start": "node app.js" - }, - "author": "Hiren Shah", - "license": "MIT", - "dependencies": { - "@adobe/jwt-auth": "^0.3.0" - } -} diff --git a/samples/adobe-jwt-node/readme.md b/samples/adobe-jwt-node/readme.md deleted file mode 100644 index 3c5d23b9..00000000 --- a/samples/adobe-jwt-node/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -# Steps -1. Download and extract the repo - -2. Edit the config files and add API Key, Client Secret, Org ID, Technical Account ID and Private key from your Adobe Developer Console integration. - -3. Execute below commands - -```npm install``` - - -```node app.js``` diff --git a/samples/adobe-jwt-php/AccessTokenProvider.php b/samples/adobe-jwt-php/AccessTokenProvider.php deleted file mode 100644 index fd9a3741..00000000 --- a/samples/adobe-jwt-php/AccessTokenProvider.php +++ /dev/null @@ -1,227 +0,0 @@ - 'JWT', 'alg' => self::SIGN_ALGORITHM]; - - $headerJson = json_encode($header); - $segments[] = $this->urlSafeB64Encode($headerJson); - - $payloadJson = json_encode($payload); - echo "Paylod JSON:\n" . $payloadJson . "\n\n"; - $segments[] = $this->urlSafeB64Encode($payloadJson); - - //now going to use openssl_sign() - $result = openssl_sign(implode('.', $segments), - $signature, - $key, - 'sha256'); - if (false === $result) { - throw new \RuntimeException('Failed to encrypt value. ' . implode("\n", $this->getSslErrors())); - } - $segments[] = $this->urlSafeB64Encode($signature); - - return implode('.', $segments); //PACK THE ARRAY CONTAINING JWT - - } - - /** - * Encode a string with URL-safe Base64. - * - * @param string $input The string you want encoded - * - * @return string - */ - private function urlSafeB64Encode(string $input): string - { - return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); - } - - - /** - * Builds request JWT. - * - * @param string $formattableTimeString - * @param string $issuer - * @param string $subject - * @param string $audience - * @param string $metascopes - * @return string[] - */ - public function buildJWTPayload($formattableTimeString, $issuer, $subject, $audience, $metascopes) - { - - $data = [ - "exp" => strtotime($formattableTimeString), - "iss" => $issuer, - "sub" => $subject, - "aud" => $audience - ]; - - if (is_array($metascopes)) { - foreach ($metascopes as &$aMetascope) { - $data[METASCOPE_ENDPOINT_PREFIX . $aMetascope] = true; - } - } else { -// single metascope - $data[METASCOPE_ENDPOINT_PREFIX . $metascopes] = true; - } - - return $data; - } - -} - - -/** Authenticates with Adobe IO - returns Auth Response - * @param $jwt - * @param $client_id - * @param $client_secret - * @return mixed | - */ -function doAdobeIOAuth($jwt, $client_id, $client_secret) -{ - $curl = curl_init(); - curl_setopt($curl, CURLOPT_POST, 1); - curl_setopt($curl, CURLOPT_POSTFIELDS, array( - "client_id" => $client_id, - "client_secret" => $client_secret, - "jwt_token" => $jwt, - - )); - curl_setopt($curl, CURLOPT_URL, AUTH_ENDPOINT); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - - $result = curl_exec($curl); - - curl_close($curl); - - return $result; -} - -/** psvm - example implementation - * Usage: - * AccessTokenProvider.php -i -s -k -u -b -c -e " - */ -function psvm() -{ - - $cl_options = "" - . "i:" // Client Id - . "s:" // Client Secret - . "k:" // Key File path - . "u:" // Issuer - . "b:" // Subject - . "c:" // metascopes - . "e::"; // expires - - $show_usage = false; - $cmdLineOpts = getopt($cl_options); - - if (isset($cmdLineOpts["i"])) { - $client_id = $cmdLineOpts["i"]; - if (isset($cmdLineOpts["s"])) { - $client_secret = $cmdLineOpts["s"]; - if (isset($cmdLineOpts["k"])) { - $key_file = $cmdLineOpts["k"]; - echo "Key file is " . $key_file . "\n"; - if (is_file($key_file) && is_readable($key_file)) { - $fHandle = fopen($key_file, "r") or die("Unable to read the key file " . $key_file . "\n"); - $private_key = fread($fHandle, filesize($key_file)) or die("Unable to read the key file " . $key_file . "\n"); - fclose($fHandle); - if (isset($cmdLineOpts["u"])) { //Checking issuer - $issuer = $cmdLineOpts["u"]; - if (isset($cmdLineOpts["b"])) { //checking subject - $subject = $cmdLineOpts["b"]; - $audience = AUD_ENDPOINT_PREFIX . $client_id; //preparing audience - if (isset($cmdLineOpts["c"])) { //checking metascopes - - $metascopes = preg_split("/,/", $cmdLineOpts["c"]); - - if (isset($cmdLineOpts["e"])) { - $exp_time = $cmdLineOpts["e"]; - } else { - echo "exp_time not provided assuming 1 day\n"; - $exp_time = '1 Day'; - } - - $jwtInstance = new JWTProvider(); - - $payload = $jwtInstance->buildJWTPayload($exp_time, - $issuer, - $subject, - $audience, - $metascopes); - - $jwt = $jwtInstance->encode($payload, $private_key); - - echo "JWT Prepared:" . $jwt . "\n\n"; - - $result = doAdobeIOAuth($jwt, $client_id, $client_secret); - - echo "Result of Auth:\n\n" . $result . "\n"; - } else { - echo "Metascopes not provided\n"; - $show_usage = true; - } - } else { - echo "Subject not provided\n"; - $show_usage = true; - - } - } else { - echo "Issuer not provided\n"; - $show_usage = true; - - } - } else { - echo "Unable to read the key file " . $key_file . '\n'; - } - } else { - echo "Key file location not provided\n"; - $show_usage = true; - } - } else { - echo "Client-secret not provided\n"; - $show_usage = true; - } - } else { - echo "Client ID not provided\n"; - $show_usage = true; - } - - if ($show_usage) { - echo "Usage:\n AccessTokenProvider.php -i -s -k -u -b -a -c -e "; - } -} - -psvm(); -exit(1); \ No newline at end of file diff --git a/samples/adobe-jwt-php/readme.md b/samples/adobe-jwt-php/readme.md deleted file mode 100644 index f089ca24..00000000 --- a/samples/adobe-jwt-php/readme.md +++ /dev/null @@ -1,20 +0,0 @@ -# PHP JWT Provider and auth example - -This is a PHP implementation to prepare JWT and shows an example login to AdobeIO to obtain `access_token`, it does not use any external library and calls `openssl_sign` and other functions available in PHP to build JWT. - -The method `doAdobeIOAuth($jwt,$client_id,$client_secret)` should be used as reference for login, it uses prepared JWT to do an authentication on AdobeIO and fetch `access_token`. - -**To Run** -1. Download and extract the repo - -2. Run PHP AccessTokenProvider.php to auth and get AdobeIO Access Token - -3. Execute below commands - - **Usage** - - ```AccessTokenProvider.php -i -s -k -u -b -c -e ``` - - **Example** - - ```php JWT/samples/adobe-jwt-php/AccessTokenProvider.php -i"e1b8b4c4109c48....." -s"965f8635-........" -k"/Path/of/private.key" -u"DD0E3......@AdobeOrg" -b"D36......@techacct.adobe.com" -c"ent_reactor_sdk,ent_adobeio_sdk" -e"1 day"``` diff --git a/samples/adobe-jwt-python/jwtencode.py b/samples/adobe-jwt-python/jwtencode.py deleted file mode 100644 index 25dc63e4..00000000 --- a/samples/adobe-jwt-python/jwtencode.py +++ /dev/null @@ -1,40 +0,0 @@ -import datetime -import json -import jwt -import os -import requests - -# Config Data -url = 'https://ims-na1.adobelogin.com/ims/exchange/jwt' -jwtPayloadRaw = """{ "iss": "{The issuer, your Organization ID from the Adobe Developer Console integration, in the format org_ident@AdobeOrg}", - "sub": "{The subject, your Technical Account ID from the Adobe Developer Console integration, in the format: id@techacct.adobe.com}", - "{The API-access claim configured for your organization: https://ims-na1.adobelogin.com/s/ent_analytics_bulk_ingest_sdk}": true, - "aud": "{The audience for the token, your API Key from the Adobe Developer Console integration, in the format: https://ims-na1.adobelogin.com/c/api_key}" }""" -jwtPayloadJson = json.loads(jwtPayloadRaw) -jwtPayloadJson["exp"] = datetime.datetime.utcnow() + datetime.timedelta(seconds=30) - -accessTokenRequestPayload = {'client_id': '{Your Client Id (API Key)}' - ,'client_secret': 'Your Client Secret'} - -# Request Access Key -#This Needs to point at where your private key is on the file system -keyfile = open(os.path.join(os.path.expanduser('~'),'.ssh/private.key'),'r') -private_key = keyfile.read() - -# Encode the jwt Token -jwttoken = jwt.encode(jwtPayloadJson, private_key, algorithm='RS256') -#print("Encoded JWT Token") -#print(jwttoken.decode('utf-8')) - - -# We are making an HTTP request similar to this curl request -#curl -X POST -H "Content-Type: multipart/form-data" -F "client_id=CLIENT_ID" -F "client_secret=CLIENT_SECRET" -F "jwt_token=`./jwtenc.sh`" https://ims-na1.adobelogin.com/ims/exchange/jwt -accessTokenRequestPayload['jwt_token'] = jwttoken -result = requests.post(url, data = accessTokenRequestPayload) -resultjson = json.loads(result.text); -#print("Full output from the access token request") -#print(json.dumps(resultjson, indent=4, sort_keys=True)) - -# Echo out the access token -print(resultjson["access_token"]); - diff --git a/samples/adobe-jwt-python/readme.md b/samples/adobe-jwt-python/readme.md deleted file mode 100644 index 02b1b064..00000000 --- a/samples/adobe-jwt-python/readme.md +++ /dev/null @@ -1,6 +0,0 @@ -This is a simple example of how to get an access token in python - -You will need to install the necessary dependencies -```pip install PyJWT``` - -You will also need to replace all of the variables encapsulated in {} with your own values diff --git a/src/pages/config.md b/src/pages/config.md index 70088438..61bbd240 100644 --- a/src/pages/config.md +++ b/src/pages/config.md @@ -40,11 +40,11 @@ - [Migration Guide](guides/authentication/ServerToServerAuthentication/migration.md) - [Migration FAQs](guides/authentication/ServerToServerAuthentication/faqs.md) - [API Reference](guides/authentication/ServerToServerAuthentication/ims.md) - - [Service Account (JWT) credential](guides/authentication/JWT/index.md) - - [Create a Public Key Certificate](guides/authentication/JWT/jwt-certificate.md) - - [JWT Metascopes](guides/authentication/JWT/scopes.md) - - [JWT Sample Code](guides/authentication/JWT/samples.md) - [API Key Authentication](guides/authentication/APIKeyAuthentication/index.md) + - [Admin Authentication](guides/authentication/AdminAuthentication/index.md) + - [Implementation Guide](guides/authentication/AdminAuthentication/implementation.md) + - [Sample Code](guides/authentication/AdminAuthentication/samples.md) + - [API Reference](guides/authentication/AdminAuthentication/ims.md) - [Tools](guides/authentication/Tools/index.md) - [OAuth 2.0 Playground](guides/authentication/Tools/o-auth-playground.md) - [Postman](guides/authentication/Tools/postman.md) diff --git a/src/pages/guides/authentication/AdminAuthentication/implementation.md b/src/pages/guides/authentication/AdminAuthentication/implementation.md new file mode 100644 index 00000000..ebd52bb3 --- /dev/null +++ b/src/pages/guides/authentication/AdminAuthentication/implementation.md @@ -0,0 +1,212 @@ +# Enterprise Web App credential implementation guide + +The following guide goes over finer implementation details for the Enterprise Web App credential. Before you proceed, we recommend you become familiar with [admin authentication](index.md) and the [Enterprise Web App credential overview](index.md#enterprise-web-app-credential). + +## Table of contents + ++ [Prerequisites and set up instructions](#prerequisites-and-set-up-instructions) ++ [Implementing the consent workflow](#implementing-the-consent-workflow) + + [Step 1: Building the consent URL](#step-1-building-the-consent-url) + + [Step 2: Verifying the redirect](#step-2-verifying-the-redirect) + + [Step 3: Generating access tokens after the admin consents](#step-3-generating-access-tokens-after-the-admin-consents) ++ [Rotating client secrets](#rotating-client-secrets) ++ [Understanding key concepts of the Enterprise Web App Credential](#understanding-key-concepts-of-the-enterprise-web-app-credential) + + [Default redirect URI and redirect URI pattern](#default-redirect-uri-and-redirect-uri-pattern) + + [Implementing security features during the redirect](#implementing-security-features-during-the-redirect) + + [Refreshing access tokens](#refreshing-access-tokens) + + [What happens when admin revokes consent](#what-happens-when-admin-revokes-consent) + + [Testing the app before publishing](#testing-the-app-before-publishing) + + [Restrictions after you publish the app](#restrictions-after-you-publish-the-app) + + +## Prerequisites and set up instructions + + + +Note: You must be an Adobe Technology Partner Program (TPP) partner to use the Enterprise Web App credential. + +1. If you're a developer or system admin on an Adobe Technology Partner program partner org, you can log in to the [Adobe Developer Console](https://developer.adobe.com/console/) and visit the [APIs and Services](https://developer.adobe.com/console/41528/servicesandapis) page. + +![](../../../images/enterprise-web-app-apis-and-services.png) + +2. Find the API or service with which you wish to integrate and click on Create Project. If Admin authentication is available for the API you selected, it will appear as an option. If not, contact your Adobe representative to log an enhancement request. + +![](../../../images/enterprise-web-app-admin-auth.png) + +3. Once you select Admin authentication, the Enterprise Web App credential will be automatically selected on the next screen. Here you can supply the name of your app as it will appear on the Consent screen during testing. You can change your app name later and also at the time of app submission. + +![](../../../images/enterprise-web-app-credential-name.png) + +4. On the next screen supply the default redirect URI and redirect URL patterns your app supports for the consent workflow. You can supply `https://localhost` or `https://localhost:` for local development. You can change the redirect URLs later too. + +![](../../../images/enterprise-web-app-redirect-url.png) + +5. On the next screen you'll be shown the scopes available to your app. Once you hit save, a project will be created for you with an Enterprise Web App credential and the selected API. You can now begin implementing the consent workflow and access token generation. + +![](../../../images/enterprise-web-app-credential-overview.png) + + +## Implementing the consent workflow + +### Step 1: Building the consent URL + +The consent workflow starts when the customer admin visits the partner app and clicks on the 'Connect with Adobe' button. You must construct the consent URL and embed it into the 'Connect with Adobe' button. To construct the consent URL, follow these steps: + +1. The Adobe IMS consent endpoint for the Enterprise Web App credential is [https://id.adobe.com/consent](https://id.adobe.com/consent). +2. Append these query parameters to the consent URL: `client_id`, `scope`, `state`, `nonce`, and optionally `redirect_uri`. + 1. Copy the value of `client_id` and `scope` from the Enterprise Web App credential overview page. + 2. Generate cryptographically secure random values for the `state` and `nonce` parameters. Store these securely in the user’s session on your backend. Furthermore, to retrieve the user's session later, store the session identifier in the user's browser (such as a secure cookie or encrypted local storage). + 3. Optionally specify a `redirect_uri` in the consent URL to redirect the admin to a URL different from your default redirect URI. The supplied URL must match one of the redirect URL patterns configured in the credential. +3. Embed the consent URL in the 'Connect with Adobe' button for the admin to click. + +### Step 2: Verifying the redirect + +Once the admin provides consent and is redirected back to your app, a few query parameters will be appended to the redirect URL containing information on whether the admin consented to your app or not. The query parameters will also contain information critical to verifying the authenticity of the redirect. + +1. The `admin_consent` parameter is set to `true` if the admin provided consent to your application, and `false` if the admin cancelled the workflow. + + The `admin_consent` parameter will not be present in the redirect in cases of error. Instead the `error` parameter will be present and the error code will be supplied as the value. Look at the [API reference](ims.md#error-codes) to view all error codes and what they mean. + +2. The `state` parameter is set to the value you supplied in the consent URL. The parameter is used to prevent Cross-site Request Forgery (CSRF) attacks. To validate it: + 1. Send the `state` parameter and the user's session ID (stored in browser cookies or local storage) to your backend server. + 2. On your backend server, compare the state value in the redirect to the version saved in the user’s session on your server. + 3. If the values do not match, you must terminate the consent workflow and reject the redirect. + +3. The `id_token` parameter is only present if the admin provided consent to your application. To validate it: + 1. Send the `id_token` parameter and the user's session ID (stored in browser cookies or local storage) to your backend server. + 2. On your backend server, inspect the `id_token` and validate its signature (view [sample code](samples.md)). + 3. On your backend server, extract the value of the `nonce` claim from the `id_token` (view [sample code](samples.md)). Compare the value of the `nonce` claim to the version saved in the user’s session on your server. + 4. If the signature of the `id_token` is not valid or the value of the `nonce` claim does not match, you must terminate the consent workflow and reject the redirect. + + + +Verifying the redirect is critical to the security of your application and Adobe customer data. View our [code samples](samples.md) (available in [NodeJS](samples.md#nodejs), [Python](samples.md#python), and [Java](samples.md#java)) to learn how to implement the verification logic in your application. + + +### Step 3: Generating access tokens after the admin consents + +Once you have verified the redirect, you are ready to generate access tokens. At this stage, your backend system can persist the mapping between the customer account and the Adobe org id. + + + +While the Adobe customer org ID is easily available through other means, you must never trust an org id value which was not extracted from a valid `id_token` received in the redirect. Trusting an org id from another source opens up your app to be exploited by a malicious customer, who can link their account to another customer's org and exchange data. + + +The customer org id can be extracted from the `org_id` claim in the `id_token`. The value of the `org_id`, along with your `client_id`, `client_secret`, and `scopes` are then used to generate access tokens on behalf of the customer. + +The following cURL command generates access tokens for the technical account set up in the customer org. The HTTP response for the cURL request contains +1. The `access_token` you can use to call Adobe APIs on the customer's behalf. +2. The `expires_in` value in seconds that determines how soon the access token will expire. + +```cURL +curl -X POST 'https://ims-na1.adobelogin.com/ims/token/v3' + -H 'Content-Type: application/x-www-form-urlencoded' + -d 'grant_type=client_credentials' + -d 'client_id=' + -d 'client_secret=' + -d 'scope=' + -d 'org_id=' +``` + +Sample response +```JSON +{ + "access_token": "ey.....", + "expires_in": 3599 +} +``` + +## Rotating client secrets + +Rotating client secrets periodically is recommended because your application will deal with Adobe customer data. Furthermore, you must rotate your client secret immediately if you believe it has been compromised. + +Client secret for the Enterprise Web App credential can be rotated through the Dev Console UI. To rotate client secrets through the UI, follow the steps below on the Enterprise Web App credential overview screen: + +1. Add a new client secret to your credential. + +![](../../../images/enterprise-web-app-client-secret.png) +![](../../../images/enterprise-web-app-client-secret-add.png) + + +2. Update your application to replace your old client secret with the new one you added. +3. Check the client secret last used timestamp to make sure your application is no longer using the old client secret. +4. Once you've confirmed your application is using the new secret, you can safely delete the old one. + + + +Once a client secret is deleted, you cannot restore it. So be extra sure you have replaced the old client secret with the new one in all locations. + +![](../../../images/enterprise-web-app-client-secret-delete.png) + +### Rotating client secrets programmatically +Programmatic rotation of Enterprise Web App client secrets is not currently supported. + +## Understanding key concepts of the Enterprise Web App Credential + +### Default redirect URI and redirect URI pattern + +Once the consent screen loads the admin can provide consent to your app or cancel the workflow. In either case and even in cases of error, the admin will be redirected back to your application. + +The default redirect URI is used if no specific `redirect_uri` is passed in the consent URL. It is also used in case an error occurs during the consent workflow. The default redirect URI must be an absolute HTTPS URL without wildcards, up to 256 characters. For example: `https://localhost`, `https://localhost:8000`, `https://example.com/redirect`. + +However, if a `redirect_uri` was specified in the consent URL and matches one of the redirect URL patterns configured in your credential, Adobe will redirect the admin to the specified redirect URL. + +The redirect URL pattern is a comma-separated list of URIs with wildcards used to validate any `redirect_uri` specified in the consent URL. The redirect URL pattern can be up to 512 characters. It must contain `https` URLs and supports wildcards to combine multiple redirect URLs together. + +As each redirect URI pattern is treated as a regex, any Periods `.` in the pattern must be escaped as `\\.`. For security reasons, wildcards are not allowed in subdomains or HTTP port, they're only allowed in the HTTP path. For example: `https://data\\.myapp\\.com/redirect/*`. + +### Implementing security features during the redirect + +Once the admin provides consent and is redirected back to your app, you must verify that the redirect is legitimate and triggered by Adobe. + +1. Read the section on Implementing the consent workflow, especially [Step 2: Verifying the redirect](#step-2-verifying-the-redirect) to understand verification steps in detail. +2. View the [sample code](samples.md) available in NodeJS, Python, and Java to implement the verification logic in your application. +3. Ensure the verification logic is implemented in the backend server. + + +### Refreshing access tokens + +The Enterprise Web App credential enables partner apps to generate access tokens, however, no refresh tokens are generated. This is because the partner app can use its `client_id`, `client_secret`, `scope`, and the customer `org_id` at any time to generate another access token. Refresh tokens are simply not required. + +### What happens when admin revokes consent + +As soon as a customer admin revokes consent, the partner app can no longer generate access tokens on behalf of that customer. Any existing access tokens may continue to work for up to an hour. + +Furthermore, when the consent is revoked, the technical account created in the customer org during the consent is also deleted. + +Lastly, since the partner app is not notified when the consent is revoked, we recommend that your app handles token errors gracefully and prompt for reauthorization if needed. + + + +The partner app will cease to have any access to the customer data within an hour of the admin revoking the consent. + +### Testing the app before publishing + +After you have created a Project on the Developer Console and set up an Enterprise Web App credential, you can add a Test technical account to the credential. + +Usually when a customer installs a partner built app, the partner owns the credential and the customer owns the technical account. However, for testing, the partner can "install" the app in their own organization and a test technical account will be created. + +You can create a test technical account in several ways: +1. Click on the 'Generate access token for testing' button on the Enterprise Web App Credential overview page. +2. Use the credential playground - 'Learn how to generate access token' tab on the Enterprise Web App Credential overview page. +3. Provide consent to your own application by manually visiting the consent URL as an admin in your own organization. + +Once a test technical account is set up, you can assign product profiles to it by visiting the Test technical account tab on the Enterprise Web App Credential overview page. + +Your organization's admin can view and manage the product profiles assigned to the test technical account on the [Adobe Admin Console](https://adminconsole.adobe.com) > Users > API credentials tab. + +Note: + +1. Adding or removing product profiles to the test technical account has no effect on the product profiles customer admins assign to the technical accounts in their orgs. +2. Furthermore, removing the test technical account from the Enterprise Web App credential does not affect any technical accounts on customer orgs that were created when customer admins provided consent to your app. + +### Restrictions after you publish the app + +To safeguard Adobe customer data, Adobe only allows customers to consent to your app after your app has been reviewed by Adobe. Each app starts out 'In Development' and is promoted to 'In Production' only after it passes the Adobe review. + +So, once you finish developing your app and are ready to publish, you must fill out listing details and submit the app for Adobe review. See our [submission guide](https://www.adobe.com/go/dd_ExperienceCloud_Submissions) for instructions. + +After you submit your app for review or once the app is published, the following restrictions apply to the Enterprise Web app credential in your Project. + +1. You cannot add or remove APIs connected to the Enterprise Web App credential. +2. You cannot remove the Adobe Exchange redirect URL pattern added to the list of redirect URL patterns. \ No newline at end of file diff --git a/src/pages/guides/authentication/AdminAuthentication/ims.md b/src/pages/guides/authentication/AdminAuthentication/ims.md new file mode 100644 index 00000000..0f58bb5a --- /dev/null +++ b/src/pages/guides/authentication/AdminAuthentication/ims.md @@ -0,0 +1,81 @@ +# Enterprise Web App Credential API Reference + +Following is an API reference for Adobe Identity Management Services (IMS) APIs. + +## Consent URL + +### Base URL + +```text +https://id.adobe.com/consent +``` + +### Query parameters + +| Query Parameter | Required | Description | Example | +|-----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------| +| client_id | Yes | The Client ID of the partner app from the Enterprise Web App credential overview page. | abcd1234 | +| scope | Yes | Comma-separated list of scopes you are requesting. Must be a subset of scopes listed on the Enterprise Web App credential overview page. | openid,AdobeId | +| state | Yes | Cryptographically secure random string generated by the partner app for protection against CSRF attacks. | xyz987 | +| nonce | Yes | Cryptographically secure random string generated by the partner app for protection against replay attacks. | nonce123 | +| redirect_uri | Optional | The URL to which Adobe should redirect the admin after the consent workflow ends. This URL must match one of the redirect URL parameters configured in the Enterprise Web App credential. | https://example.com/redirect | + + +## Redirect URL + +### Query paramters + +| Query Parameter | Always present | Description | Possible values | +|-----------------|----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------| +| admin_consent | No | Denotes whether the admin consented to the partner app or not. This parameter is not present in the redirect if there was an error during the consent workflow | true, false | +| error | No | Denotes the error which occurred during the consent workflow. This parameter is only present if there was an error in the redirect workflow. | See table below for all possible values. | +| state | Yes | Adobe echoes back the value of the state parameter you supplied | Same as the value you supplied in the consent URL. | +| id_token | No | Adobe provides an id token to enable the partner app to generate access tokens. This parameter is only present if the admin provided consent to your application. | A well formed JSON web token. | + + +### Id Token Claims + +| Claim Name | Description | Type | +|------------------|---------------------------------------------------------------------------------------------------------------------------------------|------------------------------------| +| iss | The issuer of the id token. This will always be `https://ims-na1.adobelogin.com/ims` | String | +| sub | The subject. More specifically, it's the technical account id of the consenting org. | String | +| aud | The token audience, or the application that is supposed to use this token. This will always be the client ID of the partner app. | String | +| exp | Unix seconds timestamp representing the expiry date of the token | Integer | +| iat | Unix seconds timestamp representing the timestamp when the token was issued | Integer | +| org_id | The organization ID of the customer who provided consent to the partner app. This is used to generate access tokens for this customer. | String | +| nonce | The nonce value provided in the consent URL. This is used to protect against replay attacks. | String | + +### Error codes + + +| Error Code | Description | +|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| invalid_scopes | Returned when the partner app requests one or more scopes that are not available to it. | +| invalid_redirect_uri | Returned when the redirect URI provided in the consent URL does not match the redirect URL pattern configured on the Enterprise Web App credential. | +| insufficient_privilege | Returned when the logged in user is not an admin and does not have the appropriate role to grant consent to the partner app. | +| incompatible_account_type | Returned when the logged in user account is not a company or school account. Note: only company or school accounts can provide consent to a partner app. | +| missing_state_param | Returned when the partner app did not provide a state query parameter in the consent URL. | +| error | Returned when an unknown error occurred. + + +## Generate Access Token Request + +### cURL Request + +```cURL +curl -X POST 'https://ims-na1.adobelogin.com/ims/token/v3' + -H 'Content-Type: application/x-www-form-urlencoded' + -d 'grant_type=client_credentials' + -d 'client_id=' + -d 'client_secret=' + -d 'scope=' + -d 'org_id=' +``` + +### Sample response +```JSON +{ + "access_token": "ey.....", + "expires_in": 3599 +} +``` \ No newline at end of file diff --git a/src/pages/guides/authentication/AdminAuthentication/index.md b/src/pages/guides/authentication/AdminAuthentication/index.md new file mode 100644 index 00000000..74f17bff --- /dev/null +++ b/src/pages/guides/authentication/AdminAuthentication/index.md @@ -0,0 +1,61 @@ +# Admin authentication + +Admin authentication enables partner-built apps to read and modify Adobe enterprise customer data with their explicit consent. + +If you are an Adobe Technology Partner Program (TPP) partner and your application needs to read or modify data owned by an Adobe enterprise customer organization, you can use an Admin authentication credential. + +However, before your application can access customer data, an administrator from the customer’s organization must explicitly grant consent to your application and assign relevant product profiles. + +Note: The admin always remains in control and can modify the assigned product profiles or revoke your app's consent at any time. + + + +Admin authentication and the Enterprise Web App credential is only available to Adobe Technology Partner Program (TPP) partners. + + +## Whose data can you access with Admin authentication? + +Admin authentication enables partner-built apps to read and modify Adobe enterprise customer data. Previously, enterprise customer data could only be manipulated through server to server authentication. Therefore, a customer had to build the app themselves or plug in their server-to-server credentials in partner-built apps. + +With Admin authentication a partner application can use a single credential, yet multiple customers can install the app. The customer no longer needs to supply their credentials to partner apps, thereby, strengthening their security posture. Furthermore, the partner apps built with Admin authentication are click-to-install apps which can be installed without the help of an IT department on the customer organization. + +To better understand the nuances of admin authentication, let's compare it to other supported authentication types. + +| | Who builds the app? | What data can the app access? | How is data access governed? | +|---------------------------------|----------------------------------|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Admin authentication | Adobe Technology Partner Program partner | Adobe enterprise customer data | Customer admin can limit data access using product profiles. The customer admin needs to consent to the app first. The customer admin can revoke consent at any time. | +| Server to server authentication | Adobe enterprise customer | Adobe enterprise customer data | Admins and developers can limit data access using product profiles. Data access can be removed at any time by removing product profiles or deleting the project. | +| User authentication | Adobe partner | Adobe end user data | The app requests access to data by requesting specific scopes. The Adobe end user needs to consent to the app and the list of scopes to grant access to the app. The user can revoke consent at any time. | + + +## Enterprise Web App credential + +Adobe supports the Enterprise Web App credential for admin authentication, allowing partners to build click-to-install applications that interact securely with Adobe enterprise customer data. + +Once a customer admin installs the app and provides consent to it, a technical account is set up in the customer org and linked to the partner app. The customer admin can control what data the partner app can access by managing the product profiles assigned to the technical account. Meanwhile, the partner app can generate access tokens for this technical account by using its own client id and secret. + +To safeguard customer data, the Enterprise Web App credential requires the partner app to have a secure backend server. The backend server is responsible for implementing the security features of credential and generating access tokens. + +### How does it work? + +The following diagram depicts the high level workflow through which a partner app can generate access tokens for the technical accounts in customer orgs. + +![](../../../images/enterprise-web-app-generate-access-token-uml.png) + +1. The workflow starts when the customer admin visits the partner app and clicks on the Connect with Adobe button to connect their Adobe organization to the partner app. +2. The customer admin is redirected to the Adobe IMS consent screen to provide consent to the partner app. Once the admin consents to the partner app to access their org's data, a technical account is created in the customer organization. +3. After the admin provides consent, the admin is redirected back to the partner app. The redirect URL was supplied by the partner during Enterprise Web App credential set up. +4. The partner app receives the redirect and verifies that it came from Adobe by validating the `id_token`, `state`, and `nonce` parameters in the redirect. +5. If verification passes, the partner app links the customer org to the logged in account. At this point, the partner app can use its `client_id` and `client_secret` and the customer `org_id` to generate access tokens. +6. Finally, the customer admin has completed the consent workflow and connected their Adobe org to the partner app. The customer admin can now navigate to the [Adobe Exchange manage page](https://exchange.adobe.com/manage) and assign product profiles to the newly set up technical account. + + + +Note: The customer admin can visit the [Adobe Exchange manage page](https://exchange.adobe.com/manage) to revoke consent to the partner app at any time. After the admin revokes consent, the partner app can no longer generate access tokens on this customer's behalf. All existing tokens will stop working within an hour. + + +## Development Next Steps + +1. Read the [implementation guide](implementation.md) to start implementing the Enterprise Web App credential. +2. Read the [API Reference](ims.md) to view details about the token generation request, consent URL parameters, and supported parameters & error codes in the redirect. +3. Read the [submission guide](https://www.adobe.com/go/dd_ExperienceCloud_Submissions) to submit your app for Adobe review. \ No newline at end of file diff --git a/src/pages/guides/authentication/AdminAuthentication/samples.md b/src/pages/guides/authentication/AdminAuthentication/samples.md new file mode 100644 index 00000000..93bc4882 --- /dev/null +++ b/src/pages/guides/authentication/AdminAuthentication/samples.md @@ -0,0 +1,306 @@ +# Code samples to verify the redirect + +The following guide contains code samples in NodeJS, Python, and Java that can be used to verify the parameters in the redirect. + +All of the code samples on this page assume that the user's session id is stored in browser cookies (`Secure`, `HttpOnly`, and `SameSite=Strict`). Therefore, the session id will be easily available to the backend server because the browser will send the cookies in redirect request. + + + +Note: Always verify the `state`, `nonce`, and signature of the ID token on the backend. Do not expose this logic client-side. + ++ [NodeJS](#nodejs) + + [Install NodeJS packages](#install-nodejs-packages) + + [Verify the redirect - NodeJS](#verify-the-redirect---nodejs) ++ [Python](#python) + + [Install Python packages](#install-python-packages) + + [Verify the redirect - Python](#verify-the-redirect---python) ++ [Java](#java) + + [Add these dependencies to your pom.xml](#add-these-dependencies-to-your-pomxml) + + [Verify the redirect - Java](#verify-the-redirect---java) + +## NodeJS + +### Install NodeJS packages +```bash +npm install jsonwebtoken axios jose +``` + +### Verify the redirect - NodeJS + +```js +const axios = require('axios'); +const { jwtVerify, importJWK } = require('jose'); + +/** + * Verifies: + * 1. session.state === state + * 2. id token is valid + * 3. session.nonce === payload.nonce + * + * Returns org_id from token if valid + * + * @param {string} idToken - The id_token from the redirect + * @param {object} session - Contains expected `state` and `nonce` + * @param {string} state - State from the redirect + * @returns {Promise} - org_id claim from the id_token + */ +async function verifyRedirect(idToken, session, state) { + // Step 1: State check + if (session.state !== state) { + throw new Error('State mismatch'); + } + + // Step 2: Decode header to get kid + const decodedHeader = JSON.parse(Buffer.from(idToken.split('.')[0], 'base64').toString()); + if (!decodedHeader?.kid) { + throw new Error('Invalid id token: missing kid'); + } + + const kid = decodedHeader.kid; + + // Step 3: Fetch JWKS and get matching key + const keys = await fetchAdobeKeys(); + + const jwk = keys.find(k => k.kid === kid); + if (!jwk) { + throw new Error(`No matching JWK found for kid: ${kid}`); + } + + // Step 4: Import JWK and verify token + const publicKey = await importJWK(jwk, jwk.alg); + const { payload } = await jwtVerify(idToken, publicKey, { + algorithms: ['RS256'], + }); + + // Step 6: Nonce check + if (session.nonce !== payload.nonce) { + throw new Error('Nonce mismatch'); + } + + // Step 7: Return org_id + if (!payload.orgId) { + throw new Error('org_id claim missing in id_token'); + } + + return payload.orgId; +} + +/** + * Fetch Adobe IMS public keys (JWKS) + */ +async function fetchAdobeKeys() { + const response = await axios.get('https://ims-na1.adobelogin.com/ims/keys'); + return response.data.keys; +} + + + +// Example usage +(async () => { + const idToken = 'your.id.token.here'; + const session = { + state: 'xyz123', + nonce: 'abc456' + }; + const state = 'xyz123'; + + try { + const orgId = await verifyRedirect(idToken, session, state); + console.log('Verified org_id:', orgId); + } catch (err) { + console.error('Redirect verification failed:', err.message); + } +})(); + +``` + +## Python + +### Install Python packages +```bash +pip install pyjwt requests cryptography +``` + +### Verify the redirect - Python + +```python +import requests +import jwt +from jwt import PyJWKClient + +ADOBE_JWKS_URL = "https://ims-na1.adobelogin.com/ims/keys" + +def verify_redirect(id_token: str, session: dict, state: str, client_id: str) -> str: + """ + Verifies: + 1. session.state == state + 2. id token is valid via Adobe public keys + 3. session.nonce == token's nonce + 4. Returns org_id from the token if all checks pass + + :param id_token: The id_token returned from the redirect + :param session: Dict with 'state' and 'nonce' keys + :param state: The 'state' query parameter from redirect + :return: org_id claim from the id_token + :raises RedirectVerificationError: on any failure + """ + + # Step 1: Check state + if session.get('state') != state: + raise RedirectVerificationError("State mismatch") + + # Step 2: Load signing key using PyJWKClient + try: + jwk_client = PyJWKClient(ADOBE_JWKS_URL) + signing_key = jwk_client.get_signing_key_from_jwt(id_token) + except Exception as e: + raise RedirectVerificationError(f"JWK retrieval/lookup failed: {e}") + + # Step 3: Verify id_token signature + try: + decoded = jwt.decode( + id_token, + signing_key.key, + audience=client_id, + algorithms=["RS256"] + ) + except jwt.PyJWTError as e: + raise RedirectVerificationError(f"id token verification failed: {e}") + + # Step 4: Nonce check + if session.get('nonce') != decoded.get('nonce'): + raise RedirectVerificationError("Nonce mismatch") + + # Step 5: Return org_id + org_id = decoded.get('orgId') + if not org_id: + raise RedirectVerificationError("orgId claim missing in token") + + return org_id + +class RedirectVerificationError(Exception): + pass + +# Example usage +if __name__ == "__main__": + id_token = "your.id.token.here" + session = { + "state": "xyz123", + "nonce": "abc456" + } + state = "xyz123" + client_id = "your.application.client.id" + + try: + org_id = verify_redirect(id_token, session, state, client_id) + print("Verified org_id:", org_id) + except RedirectVerificationError as e: + print("Redirect verification failed:", e) +``` + +## Java + +### Add these dependencies to your pom.xml + + +```xml + + + com.auth0 + java-jwt + 4.4.0 + + + com.auth0 + jwks-rsa + 0.22.0 + + + +``` + +### Verify the redirect - Java + +```java +import com.auth0.jwk.Jwk; +import com.auth0.jwk.JwkProvider; +import com.auth0.jwk.UrlJwkProvider; +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; + +import java.net.URL; +import java.security.interfaces.RSAPublicKey; +import java.util.Map; + +public class VerifyRedirect { + + private static final String JWKS_URL = "https://ims-na1.adobelogin.com/ims/keys"; + + /** + * Verifies that: + * 1. session.state equals the redirect state + * 2. The JWT is valid using Adobe's public key + * 3. session.nonce matches the nonce in the JWT + * + * @param idToken The id token from the redirect URL + * @param session Map containing "state" and "nonce" + * @param state The state parameter from the redirect URL + * @return The org_id claim if verification succeeds + * @throws Exception if verification fails + */ + public static String verifyRedirect(String idToken, Map session, String state) throws Exception { + // Step 1: Check state + if (!state.equals(session.get("state"))) { + throw new Exception("State mismatch"); + } + + // Step 2: Decode id token to get the kid + DecodedJWT decodedJWT = JWT.decode(idToken); + String kid = decodedJWT.getKeyId(); + + // Step 3: Get the JWK matching the kid + JwkProvider provider = new UrlJwkProvider(new URL(JWKS_URL)); + Jwk jwk = provider.get(kid); + RSAPublicKey publicKey = (RSAPublicKey) jwk.getPublicKey(); + + // Step 4: Validate the id token + Algorithm algorithm = Algorithm.RSA256(publicKey, null); + JWTVerifier verifier = JWT.require(algorithm).build(); + DecodedJWT verifiedJwt = verifier.verify(idToken); + + // Step 5: Check nonce + String tokenNonce = verifiedJwt.getClaim("nonce").asString(); + if (!session.get("nonce").equals(tokenNonce)) { + throw new Exception("Nonce mismatch"); + } + + // Step 6: Return org_id + String orgId = verifiedJwt.getClaim("orgId").asString(); + if (orgId == null || orgId.isEmpty()) { + throw new Exception("orgId claim missing in token"); + } + + return orgId; + } + + // Example usage + public static void main(String[] args) { + String idToken = "your.jwt.token.here"; + Map session = Map.of( + "state", "xyz123", + "nonce", "abc456" + ); + String state = "xyz123"; + + try { + String orgId = verifyRedirect(idToken, session, state); + System.out.println("Verified org_id: " + orgId); + } catch (Exception e) { + System.err.println("Redirect verification failed: " + e.getMessage()); + } + } +} + +``` \ No newline at end of file diff --git a/src/pages/guides/authentication/JWT/faq.md b/src/pages/guides/authentication/JWT/faq.md index ac2eb305..ad4a88d2 100644 --- a/src/pages/guides/authentication/JWT/faq.md +++ b/src/pages/guides/authentication/JWT/faq.md @@ -1,12 +1,5 @@ # Frequently Asked Questions -### Where can I find the sample code for Service Account (JWT) authentication? -Code Samples are available for the following languages - -1. [Java](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-java) -2. [DotNet](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-dotnet) -3. [Python](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-python) -4. [NodeJS](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-node) -5. [PHP](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-php) - -### How to use the private key for generating a JWT? -Please copy the full content of private key including `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` to generate a correct JWT token. + + +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. \ No newline at end of file diff --git a/src/pages/guides/authentication/JWT/index.md b/src/pages/guides/authentication/JWT/index.md index 68d76a28..2b9a43ff 100644 --- a/src/pages/guides/authentication/JWT/index.md +++ b/src/pages/guides/authentication/JWT/index.md @@ -2,119 +2,4 @@ -The Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. You must migrate to the new credential by **Jun 30, 2025**, to ensure your application continues functioning. [Learn more](../ServerToServerAuthentication/migration.md). - -To establish a secure service-to-service Adobe I/O API session, you must create a JSON Web Token (JWT) that encapsulates the identity of your integration, and then exchange it for an access token. Every request to an Adobe service must include the access token in the `Authorization` header, along with the API Key (Client ID) that was generated when you created the [Service Account Integration](../service-account-integration.md) in the [Adobe Developer Console](https://developer.adobe.com/console/). - -## Authentication Workflow - -## Creating a JSON Web Token - -A JSON Web Token for Service Account authentication requires a particular set of claims, and must be signed using a valid digital signing certificate. We recommend that you use one of the publicly available libraries or tools for building your JWT. Examples are provided for some popular languages. - -### Required Claims for a Service Account JWT - -Your JWT must contain the following claims: - -| Claim | Description| -|---|---| -| exp | _Required_. The expiration parameter is a required parameter measuring the absolute time since 01/01/1970 GMT. You must ensure that the expiration time is later than the time of issue. After this time, the JWT is no longer valid. **Recommendation**: Have a very short lived token (a few minutes) - such that it expires soon after it has been exchanged for an IMS access token. Every time a new access token is required, one such JWT is signed and exchanged. This is secure approach. Longer lived tokens that are re-used to obtain access tokens as needed are not recommended. | -| iss | _Required_. The issuer, your **Organization ID** from the Adobe Developer Console integration, in the format `org_ident@AdobeOrg`. Identifies your organization that has been configured for access to the Adobe I/O API.| -| sub | _Required_. The subject, your **Technical Account ID** from the Adobe Developer Console integration, in the format: `id@techacct.adobe.com`.| -| aud | _Required_. The audience for the token, your **API Key** from the Adobe Developer Console integration, in the format: `https://ims-na1.adobelogin.com/c/api_key`.| -| Metascopes | _Required_. The API-access claim configured for your organization: [JWT Metascopes](scopes.md), in the format: `"https://ims-na1.adobelogin.com/s/meta_scope": true`| - -The following is a sample payload to be signed and encoded. - -```json -{ - "exp": 1550001438, - "iss": "C74F69D7594880280.....@AdobeOrg", - "sub": "6657031C5C095BB40A4.....@techacct.adobe.com", - "https://ims-na1.adobelogin.com/s/ent_dataservices_sdk": true, - "aud": "https://ims-na1.adobelogin.com/c/a64f5f10849a410a97ffdac8ae1....." -} -``` - -### Sign and Encode your JWT - -The JWT must be signed and base-64 encoded for inclusion in the access request. The JWT libraries provide functions to perform these tasks. - -- The token must be signed using the private key for a digital signing certificate that is associated with your API key. You can associate more than one certificate with an API key. If you do so, you can use the private key of any associated certificate to sign your JWT. For more information about private key/public certificate, see [Create a public key certificate](./jwt-certificate.md#using-the-public-key-certificate-for-service-account-integration). - -**Algorithm**: **RS256** (RSA Signature with SHA-256) is an asymmetric algorithm, and it uses a public/private key pair: the identity provider has a private (secret) key used to generate the signature, and the consumer of the JWT (i.e. Adobe Developer Console) gets a public key to validate the signature. - -### Using JWT Libraries and Creation Tools - -Most modern languages have JWT libraries available. We recommend you use one of these libraries (or other JWT-compatible libraries) before trying to hand-craft the JWT. - -Other JWT tools are publicly available, such as the [JWT.IO](https://jwt.io/), a handy web-based encoder/decoder for JWTs. - -Examples are provided for several popular languages. - -| Language | Library | -| -------- | --------------------------- | -| Java | `atlassian-jwt` `jsontoken` | -| Node.js | `jsonwebtoken` | -| Python | `pyjwt` | - -### Additional JWT Libraries and Creation Tools - -The following JWT libraries are available, in addition to the Java, Node.js, and Python libraries for which we have provided examples. - -| Language | Library | -| -------- | ----------------------------------- | -| Ruby | `ruby-jwt` | -| PHP | `firebase php-jwt` `luciferous jwt` | -| .NET | `jwt` | -| Haskell | `haskell-jwt` | - -## Exchanging JWT to retrieve an access token - -To initiate an API session, use the JWT to obtain an access token from Adobe by making a POST request to Adobe Identity Management Service (IMS). - -- Send a POST request to: - -`https://ims-na1.adobelogin.com/ims/exchange/jwt` - -- The body of the request should contain URL-encoded parameters with your Client ID (API Key), Client Secret, and JWT: - -`client_id={api_key_value}&client_secret={client_secret_value}&jwt_token={base64_encoded_JWT}` - -### Request parameters - -Pass URL-encoded parameters in the body of your POST request: - -| Parameter | Description | -| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| client_id | The API key generated for your integration. | -| client_secret | The client secret generated for your integration. | -| jwt_token | A base-64 encoded JSON Web Token that encapsulates the identity of your integration, signed with a private key that corresponds to a public key certificate attached to the integration. | - -### Responses - -When a request has been understood and at least partially completed, it returns with HTTP status 200. On success, the response contains a valid access token. Pass this token in the Authorization header in all subsequent requests to an Adobe service. - -A failed request can result in a response with an HTTP status of 400 or 401 and one of the following error messages in the response body: - -|Response|Description| -|--- |--- | -|400 invalid_client|Integration does not exist. This applies both to the client_id parameter and the aud in the JWT. The client_id parameter and the aud field in the JWT do not match.| -|401 invalid_client|Integration does not have the exchange_jwt scope. This indicates an improper client configuration. Contact the Adobe I/O team to resolve it. The client ID and client secret combination is invalid.| -|400 invalid_token|JWT is missing or cannot be decoded. JWT has expired. In this case, the error_description contains more details. The exp or jti field of the JWT is not an integer.| -|400 invalid_signature|The JWT signature does not match any certificates attached to the integration. The signature does not match the algorithm specified in the JWT header.| -|400 invalid_scope|Indicates a problem with the requested scope for the token. Specific scope problems can be:Metascopes in the JWT do not match metascopes in the binding.Metascopes in the JWT do not match target client scopes.Metascopes in the JWT contain a scope or scopes that do not exist.The JWT has no metascopes.| -|400 bad_request|The JWT payload can be decoded and decrypted, but its contents are incorrect. This can occur when values for fields such as sub, iss, exp, or jti are not in the proper format.| - - -### Example - -``` -========================= REQUEST ========================== -POST https://ims-na1.adobelogin.com/ims/exchange/jwt --------------------------- body ---------------------------- -client_id={myClientId}&client_secret={myClientSecret}&jwt_token={myJSONWebToken} -------------------------- headers -------------------------- -Content-Type: application/x-www-form-urlencoded -Cache-Control: no-cache -``` +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../ServerToServerAuthentication/implementation.md). View the [migration guide](../ServerToServerAuthentication/migration.md) to know more. diff --git a/src/pages/guides/authentication/JWT/jwt-certificate.md b/src/pages/guides/authentication/JWT/jwt-certificate.md index 3a303fea..15c9571d 100644 --- a/src/pages/guides/authentication/JWT/jwt-certificate.md +++ b/src/pages/guides/authentication/JWT/jwt-certificate.md @@ -2,43 +2,4 @@ -The Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. You must migrate to the new credential by **Jun 30, 2025**, to ensure your application continues functioning. [Learn more](../ServerToServerAuthentication/migration.md). - -Create a private key and a public certificate. Make sure you store these securely. - -## MacOS and Linux: - -Open a terminal and execute the following command: - -`openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out certificate_pub.crt` - -![Generate public certificate](../Images/auth_jwtqs_00.png "Generate Public certificate") - -## Windows: - -1. Download an OpenSSL client to generate public certificates; for example, you can try the [OpenSSL Windows client](https://bintray.com/vszakats/generic/download_file?file_path=openssl-1.1.1-win64-mingw.zip). - -2. Extract the folder and copy it to the **C:/libs/** location. - -3. Open a command-line window and execute the following commands: - - ``` - set OPENSSL_CONF=C:/libs/openssl-1.1.1-win64-mingw/openssl.cnf - - cd C:/libs/openssl-1.1.1-win64-mingw/ - - openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out certificate_pub.crt - ``` - ![Generate public certificate windows](../Images/auth_jwtqs_000.png "Generate Public certificate windows") - -4. Once you’ve completed the steps for your chosen platform, continue in the Adobe Developer Console. - -## Using the Public Key Certificate for Service Account Integration - -1. Upload the public certificate (certificate_pub.crt) as a part of creating the integration. - - ![Upload public certificate](../Images/auth_jwtqs_03.png "Upload public certificate") - -2. Your integration should now be created with the appropriate public certificate and claims. - - ![Integration created](../Images/auth_jwtqs_04.png "Integration created") +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. \ No newline at end of file diff --git a/src/pages/guides/authentication/JWT/samples.md b/src/pages/guides/authentication/JWT/samples.md index 5aad5064..16c0490a 100644 --- a/src/pages/guides/authentication/JWT/samples.md +++ b/src/pages/guides/authentication/JWT/samples.md @@ -3,21 +3,4 @@ -The Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. You must migrate to the new credential by **Jun 30, 2025**, to ensure your application continues functioning. [Learn more](../ServerToServerAuthentication/migration.md). - -The following samples demonstrate JWT generation and exchanging it with Adobe IMS endpoint to retrieve an access token. - -### Node.js Example -[Github repo](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-node) where you can find a complete sample Node.js code to generate a JWT and exchanging it with Adobe IMS Endpoint to retrieve an access token. - -### Java Example -[Github repo](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-java) where you can find a complete sample Java code to generate a JWT and exchanging it with Adobe IMS Endpoint to retrieve an access token. - -### Python Example -[Github repo](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-python) where you can find a complete sample Python code to generate a JWT and exchanging it with Adobe IMS Endpoint to retrieve an access token. - -### C#.NET Example -[Github repo](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-dotnet) where you can find a complete sample C#.NET code to generate a JWT and exchanging it with Adobe IMS Endpoint to retrieve an access token. - -### PHP Example -[Github repo](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-jwt-php) where you can find PHP code example for JWT creation and reference method that exchanges JWT with Adobe IMS Endpoint to retrieve an access token. +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. \ No newline at end of file diff --git a/src/pages/guides/authentication/JWT/scopes.md b/src/pages/guides/authentication/JWT/scopes.md index 5392b87a..8503b1b6 100644 --- a/src/pages/guides/authentication/JWT/scopes.md +++ b/src/pages/guides/authentication/JWT/scopes.md @@ -3,85 +3,4 @@ -The Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. You must migrate to the new credential by **Jun 30, 2025**, to ensure your application continues functioning. [Learn more](../ServerToServerAuthentication/migration.md). - -JSON Web Token (JWT) metascopes govern the access and privileges for service accounts. As an application developer, you will choose the set of scopes to access certain resources by specifying the scopes as part of the encoding claims for your JWTs. - -## Adobe I/O - -Metascopes for Adobe I/O Service Account APIs - -| APIs | Metascopes | -| -------------- | ----------------------------------------------------- | -| I/O Events | `https://ims-na1.adobelogin.com/s/event_receiver_api` | -| I/O Management | `https://ims-na1.adobelogin.com/s/ent_adobeio_sdk` | - -## Creative Cloud - -Metacopes for Creative Cloud Service Account APIs - -| APIs | Metascopes | -| -------------------------- | ------------------------------------------------------ | -| Adobe Stock | `https://ims-na1.adobelogin.com/s/ent_stocksearch_sdk` | -| Dimension (default) | `https://ims-na1.adobelogin.com/s/ent_default_sdk` | -| Dimension (CCE-AS version) | `https://ims-na1.adobelogin.com/s/ent_ccas_sdk` | -| Photoshop (default) | `https://ims-na1.adobelogin.com/s/ent_default_sdk` | -| Photoshop (CCE-AS version) | `https://ims-na1.adobelogin.com/s/ent_ccas_sdk` | -| Lightroom (default) | `https://ims-na1.adobelogin.com/s/ent_default_sdk` | -| Lightroom (CCE-AS version) | `https://ims-na1.adobelogin.com/s/ent_ccas_sdk` | - -## Document Cloud - -Metascopes for Document Cloud Service Account APIs - -| APIs | Metascopes | -| ------------ | -------------------------------------------------------- | -| PDF Services | `https://ims-na1.adobelogin.com/s/ent_documentcloud_sdk` | - -## Experience Cloud - -Metascopes for Experience Cloud Service Account APIs - -| APIs | Metascopes | -| ------------------------------------------------ | ---------------------------------------------------------------------- | -| Adobe Analytics | `https://ims-na1.adobelogin.com/s/ent_analytics_bulk_ingest_sdk` | -| Experience Platform | `https://ims-na1.adobelogin.com/s/ent_dataservices_sdk` | -| Campaign | `https://ims-na1.adobelogin.com/s/ent_campaign_sdk` | -| Target | `https://ims-na1.adobelogin.com/s/ent_marketing_sdk` | -| Experience Platform Launch (Admin) | `https://ims-na1.adobelogin.com/s/ent_reactor_sdk` | - - -## GDPR - -Metascopes for GDPR Service Account APIs - -| APIs | Metascopes | -| ---- | ----------------------------------------------- | -| GDPR | `https://ims-na1.adobelogin.com/s/ent_gdpr_sdk` | - -## Sensei - -Metascopes for Sensei Service Account APIs - -| APIs | Metascopes | -| ------------------- | ------------------------------------------------------- | -| Smart Content | `https://ims-na1.adobelogin.com/s/ent_smartcontent_sdk` | -| Auto Crop, Auto Tag | `https://ims-na1.adobelogin.com/s/ent_sensei_image_sdk` | - -## User Management - -Metascopes for User management Service Account APIs - -| APIs | Metascopes | -| --------------- | ----------------------------------------------- | -| User Management | `https://ims-na1.adobelogin.com/s/ent_user_sdk` | - -## Other - -Metascopes for miscellaneous Service Account APIs - -| APIs | Metascopes | -| ---------------- | ------------------------------------------------------- | -| AEM Brand portal | `https://ims-na1.adobelogin.com/s/ent_brand_portal_sdk` | -| Places | `https://ims-na1.adobelogin.com/s/ent_places_sdk` | -| Cloud Manager | `https://ims-na1.adobelogin.com/s/ent_cloudmgr_sdk` | +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. \ No newline at end of file diff --git a/src/pages/guides/authentication/ServerToServerAuthentication/faqs.md b/src/pages/guides/authentication/ServerToServerAuthentication/faqs.md index c56fee42..2cec7906 100644 --- a/src/pages/guides/authentication/ServerToServerAuthentication/faqs.md +++ b/src/pages/guides/authentication/ServerToServerAuthentication/faqs.md @@ -17,7 +17,7 @@ ### What's happening? -In May 2023, Adobe announced the deprecation and end of life of Service Account (JWT) credentials. This means that any of your integrations or custom applications using a Service Account (JWT) credential will need to migrate to the new OAuth Server-to-Server credential before Jun 30, 2025. +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. @@ -32,12 +32,12 @@ A credential is used to uniquely identify your integration to Adobe. To use an A ### Will my Adobe integrations or applications stop working immediately? -No. Any integration or application using the Service Account (JWT) credential will continue to work until June 30, 2025. See [deprecation timelines](./migration.md#deperecation-timelines). See section on [migration next steps](#migration-next-steps). +No. Any integration or application using the Service Account (JWT) credential will stop working after current certificates expire, or March 1, 2026 (whichever is earlier). See [deprecation timelines](./migration.md#deperecation-timelines). See section on [migration next steps](#migration-next-steps). ### What is the deadline to migrate to the new credential? -You must migrate your application to use the new OAuth Server-to-Server credential before June 30, 2025, to ensure your application does not face any downtime. See [deprecation timelines](./migration.md#deperecation-timelines). See [migration guide](./migration.md). +The deadline to migrate has passed. If you integration is already broken or about to break after certificates expire, you must migrate it to use the new OAuth Server-to-Server credential as soon as possible. See [deprecation timelines](./migration.md#deperecation-timelines). See [migration guide](./migration.md). @@ -45,11 +45,7 @@ You must migrate your application to use the new OAuth Server-to-Server credenti After June 30, 2025, you can no longer refresh certificates for integrations using Service Account (JWT) credentials. Your integration will stop working once existing certificates expire. -Furthermore, Adobe will automatically convert Service Account (JWT) to OAuth Server-to-Server credentials when certificates expire or on March 1, 2026, whichever comes first. - -### We can currently create new Service Account (JWT) credentials even though they are marked as deprecated. Is it recommended? -No. Creating any new Service Account (JWT) credentials is not recommended. All Service Account (JWT) credentials, whether old or new, will stop working after June 30, 2025. We recommend you avoid migrating your application again and use the new OAuth Server-to-Server credential from the beginning. See our [implementation guide](./implementation.md). - +Furthermore, Adobe will automatically convert Service Account (JWT) to OAuth Server-to-Server credentials when certificates expire or on March 1, 2026 (whichever is earlier), breaking any integration still relying on the old credentials. @@ -60,16 +56,6 @@ No. Creating any new Service Account (JWT) credentials is not recommended. All S ### What are the benefits of using the OAuth Server-to-Server credential? You can read more about OAuth Server-to-Server credentials in our [implementation guide](./implementation.md). You can view the comparison between the OAuth Server-to-Server credential and the Service Account (JWT) credential [here](./migration.md#why-oauth-server-to-server-credentials). - - - -### Can I programmatically rotate certificates for Service Account (JWT) credential? -No such ability is currently available. There are no plans to add such ability either. - -Instead, we recommend switching to the new credential that does not use expiring certificates and allows you to [rotate client secrets](./implementation.md#rotating-client-secrets) through the UI and API ([programmatically](./implementation.md#rotating-client-secrets-programmatically)). - - - ### Can I programmatically rotate client secrets for OAuth Server-to-Server credentials? Absolutely. View our guide on rotating client secrets programmatically [here](./implementation.md#rotating-client-secrets-programmatically). diff --git a/src/pages/guides/authentication/ServerToServerAuthentication/ims.md b/src/pages/guides/authentication/ServerToServerAuthentication/ims.md index bc4ab907..9f63d2bb 100644 --- a/src/pages/guides/authentication/ServerToServerAuthentication/ims.md +++ b/src/pages/guides/authentication/ServerToServerAuthentication/ims.md @@ -48,7 +48,7 @@ You do not need a refresh token for OAuth Server-to-Server credentials. You can ## List all client secrets -Pre-requisite: You need to add `I/O Management API` to your project for fetching the list of secrets. See [Add API to a Project](../../services/services-add-api-jwt.md) +Pre-requisite: You need to add `I/O Management API` to your project for fetching the list of secrets. See [Add API to a Project](../../services/services-add-api-oauth-s2s.md) Note: No `client_secret` values are returned by this API. Only the secret `uuid` and other metadata is returned. @@ -108,7 +108,7 @@ Note: the `created_at` and `last_used_at` values are in milliseconds since UNIX You can add up to 2 client secrets for an OAuth Server-to-Server credential. -Pre-requisite: You need to add `I/O Management API` to your project for adding client secret to the credential. See [Add API to a Project](../../services/services-add-api-jwt.md) +Pre-requisite: You need to add `I/O Management API` to your project for adding client secret to the credential. See [Add API to a Project](../../services/services-add-api-oauth-s2s.md) Note: The API response contains the the `client_secret` that was added and its `uuid`. This `client_secret` will never be returned in plain text by any other API response. However, you can still find it on the Developer Console UI. @@ -149,7 +149,7 @@ Note: the `created_at` and `last_used_at` values are in milliseconds since UNIX ## Remove client secret from credential -Pre-requisite: You need to add `I/O Management API` to your project for removing client secret from the credential. See [Add API to a Project](../../services/services-add-api-jwt.md) +Pre-requisite: You need to add `I/O Management API` to your project for removing client secret from the credential. See [Add API to a Project](../../services/services-add-api-oauth-s2s.md) Note: you need the secret `uuid` to delete a secret. You cannot use the plain text `client_secret` value to identify which secret to delete. diff --git a/src/pages/guides/authentication/ServerToServerAuthentication/index.md b/src/pages/guides/authentication/ServerToServerAuthentication/index.md index 368866dd..d31b9cda 100644 --- a/src/pages/guides/authentication/ServerToServerAuthentication/index.md +++ b/src/pages/guides/authentication/ServerToServerAuthentication/index.md @@ -22,17 +22,9 @@ However, no access to data is given out by default. Instead, when you create a s You can also modify the set of product profiles by returning to your project on the Adobe Developer Console. As an admin, you can manage the product profiles assigned to different applications by visiting the [Adobe Admin Console](https://adminconsole.adobe.com/) > Users > API credentials tab. -## Server to server credential types +## OAuth Server-to-Server credential -Adobe supports two server to server authentication credentials. These credentials only differ in the way your application generates the access token, the rest of their functioning is similar. - -1. [OAuth Server-to-Server credential](#oauth-server-to-server-credential) -2. [Service Account (JWT) credential *(deprecated)*](#service-account-jwt-credential-deprecated) - - -### OAuth Server-to-Server credential - -The OAuth Server-to-Server credential relies on the OAuth 2.0 `client_credentials` grant type to generate access tokens. To generate an access token, your application can make a single HTTP request with your `client_id` and `client_secret` and `scopes`. +Adobe supports the OAuth Server-to-Server to credential to perform server-to-server authentication. The OAuth Server-to-Server credential relies on the OAuth 2.0 `client_credentials` grant type to generate access tokens. To generate an access token, your application can make a single HTTP request with your `client_id` and `client_secret` and `scopes`. As the token generation logic uses your `client_secret`, this logic must be implemented on a secure backend server to prevent malicious actors from accessing your secrets. We recommend using standard OAuth libraries to implement access token generation. @@ -41,22 +33,4 @@ Read our OAuth Server-to-server credential implementation guide - 1. [Generating access tokens using cURL](./implementation.md#generate-access-tokens) 2. [Generating access tokens programmatically using standard OAuth2 libraries](./implementation.md#rotating-client-secrets-programmatically) 3. [Migrating from Service Account (JWT) credentials to OAuth Server-to-Server credentials](./migration.md) -4. [API reference](./ims.md) - - -### Service Account (JWT) credential *(deprecated)* - - - -The Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. You must migrate to the new credential by **Jun 30, 2025**, to ensure your application continues functioning. [Learn more](../ServerToServerAuthentication/migration.md). - -Service Account (JWT) credentials rely on the JWT token exchange mechanism to generate access tokens. This credential's details include two secrets a `client_secret` and a `private.key` (part of a public certificate private key pair). - -The token generation logic first requires your application to construct a JWT token signed by your private.key. This token is then exchanged for an access token by making an HTTP Request to Adobe Identity Management Services (IMS). - -As the token generation logic uses your `client_secret`, this logic must be implemented on a secure backend server to prevent malicious actors from accessing your secrets. - -Read our Service Account (JWT) credential implementation guide - - -1. [Generating access tokens programmatically](../JWT/index.md) -2. [Migrating from Service Account (JWT) credentials to OAuth Server-to-Server credentials](./migration.md) +4. [API reference](./ims.md) \ No newline at end of file diff --git a/src/pages/guides/authentication/ServerToServerAuthentication/migration.md b/src/pages/guides/authentication/ServerToServerAuthentication/migration.md index 2342c26c..6acebee1 100644 --- a/src/pages/guides/authentication/ServerToServerAuthentication/migration.md +++ b/src/pages/guides/authentication/ServerToServerAuthentication/migration.md @@ -1,10 +1,11 @@ # Migrating from Service Account (JWT) credential to OAuth Server-to-Server credential -The Service Account (JWT) credentials have been deprecated in favor of the new OAuth Server-to-Server credentials. They will reach end of life on June 30, 2025. + +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the OAuth Server-to-Server credentials. The new OAuth Server-to-Server credentials simplify all aspects of application development - experimentation, implementation, and maintenance. See the section on [Why OAuth Server-to-Server credentials?](#why-oauth-server-to-server-credentials) below to learn more. -While the Service Account (JWT) credentials have been marked as deprecated, they will continue to work until **Jun 30, 2025**. Therefore you must migrate your application or integration to use the new OAuth Server-to-Server credential before Jun 30, 2025. See the section on [deprecation timelines](#deprecation-timelines) below to learn more. +Service Account (JWT) credentials will not work after current certificates expire or March 1, 2026, whichever is earlier. Therefore, you must migrate your integration to use the new OAuth Server-to-Server credential ASAP. Migrating your applications to the OAuth Server-to-Server credential is a simple two-step process that enables a zero downtime migration for your applications and integrations. Please read our [migration guide](#migration-overview) below to familiarize yourself with the migration process. @@ -42,11 +43,6 @@ Lastly, while the new OAuth Server-to-Server credentials do not use expiring cer ## Deprecation Timelines - - -The end of life date for Service Account (JWT) credentials has been extended from Jan 27, 2025, to Jun 30, 2025, to give customers more time to migrate. You must migrate your integrations to use OAuth Server-to-Server credentials before June 30, 2025 to avoid any downtime. - - 1. **May 1, 2023** * Adobe announces the deprecation of Service Account (JWT) credentials. The deprecation timelines and the end-of-life date are also announced. * All existing integrations using Service Account (JWT) credentials continue to work. @@ -57,7 +53,7 @@ The end of life date for Service Account (JWT) credentials has been extended fro 3. **June 30, 2025 (End of Life)** * After June 30, 2025, you cannot refresh certificates for integrations using Service Account (JWT) credentials. Your integrations will stop working once existing certificates expire. - * Furthermore, Adobe will automatically convert Service Account (JWT) credentials to OAuth Server-to-Server credentials when certificates expire or on March 1, 2026, whichever comes first. + * Adobe will automatically convert Service Account (JWT) credentials to OAuth Server-to-Server credentials after certificates expire or on March 1, 2026 (whichever is earlier), breaking any integration still relying on the old credentials. ## Migration Overview diff --git a/src/pages/guides/authentication/Tools/index.md b/src/pages/guides/authentication/Tools/index.md index c21d09f1..d2bedd69 100644 --- a/src/pages/guides/authentication/Tools/index.md +++ b/src/pages/guides/authentication/Tools/index.md @@ -1,12 +1,6 @@ # Tools Overview - -## OAuth 2.0 Playground -Do you have an OAuth integration created? - -[Try the OAuth 2.0 Playground tool](o-auth-playground.md) to generate an access token. - ## Postman [Try Postman for generating an access token](postman.md) for both OAuth and Service Account Integration. diff --git a/src/pages/guides/authentication/Tools/o-auth-playground.md b/src/pages/guides/authentication/Tools/o-auth-playground.md deleted file mode 100644 index 81f74cef..00000000 --- a/src/pages/guides/authentication/Tools/o-auth-playground.md +++ /dev/null @@ -1,36 +0,0 @@ -# OAuth 2.0 Playground - -The OAuth 2.0 Playground is an Adobe internet utility that enables developers to easily obtain an OAuth 2.0 access token for use in building and testing their integrations. Before you use the OAuth 2.0 Playground, you should already have created an integration you want to use for this purpose. The OAuth 2.0 Playground source code is also available for you to view and experiment with. - -## Steps to obtain a token: - -1. Go to [OAuth 2.0 Playground](https://adobeioruntime.net/api/v1/web/io-solutions/adobe-oauth-playground/oauth.html) - - ![op-1](../Images/OP_1.png) - -2. Go to the [Adobe Developer Console](https://developer.adobe.com/console/) - -3. Create a project within Console. For complete steps to creating a project in Console, begin by reading the [Adobe Developer Console getting started guide](../../getting-started.md) and [projects overview](../../projects/index.md). - -4. Once you have created a project, you will be able to add services including APIs, Adobe I/O Events registrations, and Adobe I/O Runtime. Add an API to your project and then select the services with which you wish to integrate (such as Adobe Analytics > OAuth 2.0 Integration) - - To add an API that uses OAuth authentication and authorization, follow the steps outlined in the guide for [adding an API to a project using OAuth authentication](../../services/services-add-api-oauth.md). - - When the API has been successfully connected, you will be able to access the newly generated credentials including Client ID and Client Secret. - -5. Copy your **Client ID** (API Key) and **Client Secret** from Adobe Developer Console into the OAuth 2.0 Playground. - -6. Enter scopes as: - ```openid,read_organizations,additional_info.projectedProductContext,additional_info.job_function``` - - ![op-3](../Images/OP_3.png) - -7. Click **Generate Tokens.** - -8. You will be prompted for login by Adobe. Log in using your Adobe ID. - - ![op-4](../Images/OP_4.png) - -9. Your tokens will be generated. - - ![op-5](../Images/OP_5.png) diff --git a/src/pages/guides/authentication/Tools/postman.md b/src/pages/guides/authentication/Tools/postman.md index 5d516652..caf3ea91 100644 --- a/src/pages/guides/authentication/Tools/postman.md +++ b/src/pages/guides/authentication/Tools/postman.md @@ -37,29 +37,6 @@ openid,AdobeID,read_organizations,additional_info.projectedProductContext,additi ![pm-6](../Images/PM_6.png) -## JWT Access Token -### Steps -1. Go to [Adobe Developer Console](https://developer.adobe.com/console) - -2. Open the Service Account Integration for which you want to generate an access token. - -![pmj-1](../Images/PM_JWT_1.png) - -3. Click on the JWT tab, paste the entire private key file content including the `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` and click on `Generate JWT`. - -![pmj-2](../Images/PM_JWT_2.png) - -4. Copy the `Sample CURL command` and open Postman. (*Mac and Linux user can also paste the CURL command in terminal and get the access token.*) - -![pmj-3](../Images/PM_JWT_3.png) - -5. Click on `Import` -> `Paste Raw Text` and paste the CURL command. - -![pmj-4](../Images/PM_JWT_4.png) - -6. Click on `Send`. You will receive an access token. - -![pmj-5](../Images/PM_JWT_5.png) ## CURL Requests in Windows diff --git a/src/pages/guides/authentication/UserAuthentication/implementation.md b/src/pages/guides/authentication/UserAuthentication/implementation.md index ba94bc4a..d3709272 100644 --- a/src/pages/guides/authentication/UserAuthentication/implementation.md +++ b/src/pages/guides/authentication/UserAuthentication/implementation.md @@ -1,6 +1,6 @@ # User Authentication Implementation Guide -The following guide goes over finer implementation details for user authentication credentials. At the end of this guide is a list of recommended industry-standard OAuth2 libraries. Before proceeding, we recommend you familiarize yourself with the 3-legged OAuth flow in our [user authentication guide](../UserAuthentication/index.md). +The following guide goes over finer implementation details for user authentication credentials. At the end of this guide is a list of recommended industry-standard OAuth2 libraries. Before proceeding, we recommend you familiarize yourself with the 3-legged OAuth flow in our [user authentication guide](index.md). ## User authentication credential types diff --git a/src/pages/guides/authentication/index.md b/src/pages/guides/authentication/index.md index c51131fd..b481015a 100644 --- a/src/pages/guides/authentication/index.md +++ b/src/pages/guides/authentication/index.md @@ -17,11 +17,12 @@ Depending on the Adobe product or service you are integrating into your app, you 2. [Server to server authentication](./ServerToServerAuthentication/index.md) * [OAuth Server to Server credential](./ServerToServerAuthentication/index.md#oauth-server-to-server-credential) - * [Service Account (JWT) credential *(deprecated)*](./ServerToServerAuthentication/index.md#service-account-jwt-credential-deprecated) 3. [API key authentication](./APIKeyAuthentication/index.md) * [API Key credential](./APIKeyAuthentication/index.md#api-key-credential) +4. [Admin authentication](./AdminAuthentication/index.md) + * [Enterprise Web App credential](./AdminAuthentication/index.md#enterprise-web-app-credential) ## User authentication @@ -48,7 +49,7 @@ View our guide on server to server authentication credentials - -The Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. You must migrate to the new credential by **Jun 30, 2025**, to ensure your application continues functioning. [Learn more](./ServerToServerAuthentication/migration.md). +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. ## API key authentication @@ -59,3 +60,12 @@ View our guide on API key credentials - 1. [Understanding different uses of the API key credential](./APIKeyAuthentication/index.md#understanding-different-uses-of-the-api-key-credential) 2. [Understanding allowed origins](./APIKeyAuthentication/index.md#understanding-allowed-origins) + +## Admin authentication + +If you are an Adobe Technology Program Partner and your application needs to read or modify the data of an Adobe enterprise customer, you can do so using an admin authentication credential. However, before your application can view or edit the customer's data, a customer admin would need to provide explicit 'consent' to your application. + +View our guide on admin authentication credentials - +1. [Understanding admin authentication credentials](./AdminAuthentication/index.md) +2. [Understanding how the Enterprise Web App credential works](./AdminAuthentication/index.md#enterprise-web-app-credential) +3. [Implementing the Enterprise Web App credential](./AdminAuthentication/implementation.md) \ No newline at end of file diff --git a/src/pages/guides/authentication/service-account-integration.md b/src/pages/guides/authentication/service-account-integration.md index 3056e52d..62599f29 100644 --- a/src/pages/guides/authentication/service-account-integration.md +++ b/src/pages/guides/authentication/service-account-integration.md @@ -2,54 +2,4 @@ -Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. **You must migrate to the new credential by Jun 30, 2025 in order to ensure your application continues to function. View our [migration guide](../ServerToServerAuthentication/migration.md) to know more.** - -A Service Account connection allows your application to call Adobe services on behalf of the application itself or on behalf of an enterprise organization. - -For this type of connection, you will create a JSON Web Token (JWT) that encapsulates your credentials and begin each API session by exchanging the JWT for an access token. - -The JWT encodes all of the identity and security information required to obtain an access token and must be signed with the private key that is associated with a public key certificate specified on your integration. - -This article walks you through the steps to set up a **Service Account** connection. - -## Service Account connection workflow - -### Step 1: Create a project in Adobe Developer Console - -Integrations are now created as part of a "project" within Adobe Developer Console. For complete steps to creating a project in Console, begin by reading the [Adobe Developer Console getting started guide](../getting-started.md) and [projects overview](../projects/index.md). - -Once you have created a project, you will be able to add services including APIs, Adobe I/O Events registrations, and Adobe I/O Runtime. - -### Step 2: Add an API to your project using Service Account authentication - -To add an API that uses Service Account (JWT) authentication, follow the steps outlined in the guide for [adding an API to a project using Service Account authentication](../services/services-add-api-jwt.md). - -During the API configuration process, you will be able to generate a key pair and download the private key. - -When the API has been successfully connected, you will be able to access the newly generated credentials including Client ID and Client Secret, as well as generate an access token using the private key that you generated during configuration. - -### Step 3: Try It - -In order to try out the connection, follow the steps in the [Adobe Developer Console credentials guide](../credentials.md) for generating a JWT token and copy the *Sample cURL command* provided. - -Next, open Postman and select Import > Paste Raw Text and paste the copied curl command. Select **Import** to continue. - -![Postman import](./Images/auth_jwtqs_07.png "Postman import") - -With the command imported, select **Send** to execute. - -![Postman send](./Images/auth_jwtqs_08.png "Postman send") - -The example curl sends a POST request to [https://ims-na1.adobelogin.com/ims/exchange/jwt](https://ims-na1.adobelogin.com/ims/exchange/jwt) using the parameters outlined in the table below. - -The response body includes your newly generated access token (`access_token`). - -| Parameter | Description| -|---|---| -| `client_id` | The Client ID provided in Console as part of the *Credential details*. | -| `client_secret` | The Client Secret provided in Console as part of the *Credential details*. | -| `jwt_token` | A base-64 encoded JSON Web Token that encapsulates the identity of your connection, signed with a private key that corresponds to a public key certificate attached to the connection. This token can be generated in Console from the *Generate JWT* tab within *Credentials*. Note that this token has the expiration time parameter `exp` set to 24 hours from when the token is generated. | - - - -Check the documentation for the specific API service for which you’re hitting authenticated endpoints to find what other parameters are expected. Most of them need an `x-api-key`, which will be the same as your `client_id`. +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. \ No newline at end of file diff --git a/src/pages/guides/email-alerts/cert-expiry.md b/src/pages/guides/email-alerts/cert-expiry.md index 39ea252d..a79f6d1f 100644 --- a/src/pages/guides/email-alerts/cert-expiry.md +++ b/src/pages/guides/email-alerts/cert-expiry.md @@ -1,5 +1,13 @@ # Certificate Expiry Overview + + +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). The following guide is retained for historical reference. You will be unable to refresh certificates for Service Account (JWT) credentials anymore. + + + +If you received an email about expiring certificates, this is your last chance to [migrate your integration](../authentication/ServerToServerAuthentication/migration.md) to use an OAuth Server-to-Server credential. After your certificates expire, Adobe will automatically convert your integration to OAuth Server-to-Server credentials, breaking your integration if it still relies on the old Service Account (JWT) credential. + Several services on the Adobe Developer Console require using the Service Account (JWT) credential for service-to-service authentication. The Service Account (JWT) credential utilizes a public certificate and a private key pair in order to authenticate your custom integration or application. Custom integrations and applications need to use the certificate key pair to sign and generate a JWT token which is then exchanged for an Adobe IMS access token. Once exchanged, the application can then use the Adobe IMS access token to make calls to Adobe's APIs. @@ -12,10 +20,6 @@ The certificate key pairs generated on the Developer Console expire after a year Upon receiving the email alert, you need to replace the expiring certificate key pair with a new pair in a timely fashion to ensure all your custom integrations and applications continue to work. See this step-by-step guide on replacing certificate key pairs for more detailed instructions. - - -Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. **You must migrate to the new credential by Jun 30, 2025 in order to ensure your application continues to function. View our [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more.** - ## A step-by-step guide to replacing expiring certificate key pairs ### Step 1: Identify application and Project maintainers diff --git a/src/pages/guides/index.md b/src/pages/guides/index.md index 3ceffa1e..19e08755 100644 --- a/src/pages/guides/index.md +++ b/src/pages/guides/index.md @@ -52,7 +52,7 @@ Quickly start building an App Builder application by using the App Builder proje ### Migrate to the new OAuth Server-to-Server credential -The Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. Your applications using the Service Account (JWT) credentials will stop working after Jun 30, 2025. You must migrate to the new credential by **Jun 30, 2025**, to ensure your application continues functioning. [Learn more](./authentication/ServerToServerAuthentication/migration.md) +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. ### Email alerts diff --git a/src/pages/guides/plugins/plugin-distribution.md b/src/pages/guides/plugins/plugin-distribution.md index e2d8f86d..49cfd542 100644 --- a/src/pages/guides/plugins/plugin-distribution.md +++ b/src/pages/guides/plugins/plugin-distribution.md @@ -2,7 +2,7 @@ This guide provides instructions for distributing a Plugin created in Adobe Developer Console. -For information on how to build a plugin, please being by reading the [plugin overview](../plugins/index.md). +For information on how to build a plugin, please being by reading the [plugin overview](index.md). ## Select plugin project diff --git a/src/pages/guides/services/services-add-api-jwt.md b/src/pages/guides/services/services-add-api-jwt.md index 33be8fd6..53b782f5 100644 --- a/src/pages/guides/services/services-add-api-jwt.md +++ b/src/pages/guides/services/services-add-api-jwt.md @@ -2,4 +2,4 @@ -Service Account (JWT) credentials have been deprecated in favor of the OAuth Server-to-Server credentials. View our [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. The new version of this guide that uses OAuth Server-to-Server credentials is now available here - [**Add API to project using OAuth Server-to-Server credentials**](../services/services-add-api-oauth-s2s.md). +As of June 30, 2025, Service Account (JWT) credentials have reached their end of life and are no longer supported. All server-to-server integrations must use the [OAuth Server-to-Server credentials](../authentication/ServerToServerAuthentication/implementation.md). View the [migration guide](../authentication/ServerToServerAuthentication/migration.md) to know more. The new version of this guide that uses OAuth Server-to-Server credentials is now available here - [**Add API to project using OAuth Server-to-Server credentials**](services-add-api-oauth-s2s.md). diff --git a/src/pages/guides/services/services-add-api-key.md b/src/pages/guides/services/services-add-api-key.md index 22cb570a..9b443725 100644 --- a/src/pages/guides/services/services-add-api-key.md +++ b/src/pages/guides/services/services-add-api-key.md @@ -60,7 +60,7 @@ To learn more about insights, begin by reading the [insights overview](../insigh ## Next steps -With an API successfully added, you can follow the same workflow steps to add additional APIs, or return to the [services overview](../services/index.md) to select another type of service to add to your project. +With an API successfully added, you can follow the same workflow steps to add additional APIs, or return to the [services overview](index.md) to select another type of service to add to your project. If you have completed development on your project and are ready to submit your application for approval, please read the [project approval guide](../projects/approval.md) to get started. diff --git a/src/pages/guides/services/services-add-api-oauth-s2s.md b/src/pages/guides/services/services-add-api-oauth-s2s.md index 065c85fb..69159012 100644 --- a/src/pages/guides/services/services-add-api-oauth-s2s.md +++ b/src/pages/guides/services/services-add-api-oauth-s2s.md @@ -99,6 +99,6 @@ To learn more about insights, begin by reading the [insights overview](../insigh ## Next steps -With an API successfully added, you can follow the same workflow steps to add additional APIs, or return to the [services overview](../services/index.md) to select another type of service to add to your project. +With an API successfully added, you can follow the same workflow steps to add additional APIs, or return to the [services overview](index.md) to select another type of service to add to your project. If you have completed development on your project and are ready to submit your application for approval, please read the [project approval guide](../projects/approval.md) to get started. \ No newline at end of file diff --git a/src/pages/guides/services/services-add-api-oauth-user-authentication.md b/src/pages/guides/services/services-add-api-oauth-user-authentication.md index 90ef91b5..ed5470f9 100644 --- a/src/pages/guides/services/services-add-api-oauth-user-authentication.md +++ b/src/pages/guides/services/services-add-api-oauth-user-authentication.md @@ -129,7 +129,7 @@ To learn more about insights, begin by reading the [insights overview](../insigh ## Next steps -With an API successfully added, you can follow the same workflow steps to add additional APIs or return to the [services overview](../services/index.md) to select another type of service to add to your project. +With an API successfully added, you can follow the same workflow steps to add additional APIs or return to the [services overview](index.md) to select another type of service to add to your project. If you have completed development on your project and are ready to submit your application for approval, please read the [project approval guide](../projects/approval.md) to get started. diff --git a/src/pages/guides/services/services-add-api-oauth.md b/src/pages/guides/services/services-add-api-oauth.md index e80cf8a0..fbb3fad1 100644 --- a/src/pages/guides/services/services-add-api-oauth.md +++ b/src/pages/guides/services/services-add-api-oauth.md @@ -2,4 +2,4 @@ -The new version of this guide is now available here - [Add API to project using OAuth User Authentication credentials](../services/services-add-api-oauth-user-authentication.md). \ No newline at end of file +The new version of this guide is now available here - [Add API to project using OAuth User Authentication credentials](services-add-api-oauth-user-authentication.md). \ No newline at end of file diff --git a/src/pages/guides/services/services-add-event.md b/src/pages/guides/services/services-add-event.md index 67cf0bba..4c2d546b 100644 --- a/src/pages/guides/services/services-add-event.md +++ b/src/pages/guides/services/services-add-event.md @@ -78,6 +78,6 @@ The *Debug Tracing* tab shows details related to recent requests and responses r ## Next steps -Now that you have successfully added events to your project or workspace, you can follow this workflow again to add additional event registrations, or return to the [services overview](../services/index.md) to select another type of service to add to your project. +Now that you have successfully added events to your project or workspace, you can follow this workflow again to add additional event registrations, or return to the [services overview](index.md) to select another type of service to add to your project. If you have completed development on your project and are ready to submit your application for approval, please read the [project approval guide](../projects/approval.md) to get started. \ No newline at end of file diff --git a/src/pages/guides/services/services-enable-runtime.md b/src/pages/guides/services/services-enable-runtime.md index e2300c31..82e683e0 100644 --- a/src/pages/guides/services/services-enable-runtime.md +++ b/src/pages/guides/services/services-enable-runtime.md @@ -66,6 +66,6 @@ To learn more about insights, begin by reading the [insights overview](../insigh ## Next steps -With Runtime successfully added to your project or workspace, you can now return to the [services overview](../services/index.md) to select another type of service to add to your project. +With Runtime successfully added to your project or workspace, you can now return to the [services overview](index.md) to select another type of service to add to your project. If you have completed development on your project and are ready to submit your application for approval, please read the [project approval guide](../projects/approval.md) to get started. \ No newline at end of file diff --git a/src/pages/images/enterprise-web-app-admin-auth.png b/src/pages/images/enterprise-web-app-admin-auth.png new file mode 100644 index 00000000..b86551d1 Binary files /dev/null and b/src/pages/images/enterprise-web-app-admin-auth.png differ diff --git a/src/pages/images/enterprise-web-app-apis-and-services.png b/src/pages/images/enterprise-web-app-apis-and-services.png new file mode 100644 index 00000000..1efb555d Binary files /dev/null and b/src/pages/images/enterprise-web-app-apis-and-services.png differ diff --git a/src/pages/images/enterprise-web-app-client-secret-add.png b/src/pages/images/enterprise-web-app-client-secret-add.png new file mode 100644 index 00000000..2a38f312 Binary files /dev/null and b/src/pages/images/enterprise-web-app-client-secret-add.png differ diff --git a/src/pages/images/enterprise-web-app-client-secret-delete.png b/src/pages/images/enterprise-web-app-client-secret-delete.png new file mode 100644 index 00000000..22b7c2dc Binary files /dev/null and b/src/pages/images/enterprise-web-app-client-secret-delete.png differ diff --git a/src/pages/images/enterprise-web-app-client-secret.png b/src/pages/images/enterprise-web-app-client-secret.png new file mode 100644 index 00000000..65a3b917 Binary files /dev/null and b/src/pages/images/enterprise-web-app-client-secret.png differ diff --git a/src/pages/images/enterprise-web-app-consent-screen.png b/src/pages/images/enterprise-web-app-consent-screen.png new file mode 100644 index 00000000..28cac03b Binary files /dev/null and b/src/pages/images/enterprise-web-app-consent-screen.png differ diff --git a/src/pages/images/enterprise-web-app-credential-name.png b/src/pages/images/enterprise-web-app-credential-name.png new file mode 100644 index 00000000..378ff3b6 Binary files /dev/null and b/src/pages/images/enterprise-web-app-credential-name.png differ diff --git a/src/pages/images/enterprise-web-app-credential-overview.png b/src/pages/images/enterprise-web-app-credential-overview.png new file mode 100644 index 00000000..15d3ff5e Binary files /dev/null and b/src/pages/images/enterprise-web-app-credential-overview.png differ diff --git a/src/pages/images/enterprise-web-app-generate-access-token-uml.png b/src/pages/images/enterprise-web-app-generate-access-token-uml.png new file mode 100644 index 00000000..9dcf26cd Binary files /dev/null and b/src/pages/images/enterprise-web-app-generate-access-token-uml.png differ diff --git a/src/pages/images/enterprise-web-app-redirect-url.png b/src/pages/images/enterprise-web-app-redirect-url.png new file mode 100644 index 00000000..a104d08d Binary files /dev/null and b/src/pages/images/enterprise-web-app-redirect-url.png differ diff --git a/src/pages/support/faq.md b/src/pages/support/faq.md index 062979f7..bc789a6d 100644 --- a/src/pages/support/faq.md +++ b/src/pages/support/faq.md @@ -1,6 +1,6 @@ # Frequently Asked Questions -This document provides answers to frequently asked questions about Adobe Developer Console. This is a great place to start when troubleshooting a problem with Console. If you are unable to find the answer you're looking for, please refer to the [Support overview](../support/index.md) for additional resources. +This document provides answers to frequently asked questions about Adobe Developer Console. This is a great place to start when troubleshooting a problem with Console. If you are unable to find the answer you're looking for, please refer to the [Support overview](index.md) for additional resources. ## Questions @@ -22,21 +22,16 @@ This document provides answers to frequently asked questions about Adobe Develop - [Why can't I create a project for my organization?](#why-cant-i-create-a-project-for-my-organization) - [Why can't I add an XD plugin to my project?](#why-cant-i-add-an-xd-plugin-to-my-project) - [Why can't I add Adobe I/O Runtime to my project?](#why-cant-i-add-adobe-io-runtime-to-my-project) - - [I want to generate my own JWT. How do I do that?](#i-want-to-generate-my-own-jwt-how-do-i-do-that) - [Why do I see a Read Only label on some of my projects? Even some that I created?](#why-do-i-see-a-read-only-label-on-some-of-my-projects-even-some-that-i-created) - [Why do I see an Auto-generated label on some of my projects?](#why-do-i-see-an-auto-generated-label-on-some-of-my-projects) - - [How do I know if I should use JWT or OAuth?](#how-do-i-know-if-i-should-use-jwt-or-oauth) - [Why can't I change the name of my App Builder app?](#why-cant-i-change-the-name-of-my-app-builder-app) - [Where did my integrations and plugins go?](#where-did-my-integrations-and-plugins-go) - [Where can I find the sample code for OAuth authentication?](#where-can-i-find-the-sample-code-for-oauth-authentication) - - [How to use the private key for generating a JWT?](#how-to-use-the-private-key-for-generating-a-jwt) - [What do Default Redirect URI and the Redirect URI Pattern mean?](#what-do-default-redirect-uri-and-the-redirect-uri-pattern-mean) ### Services * [Why is the service I want to use greyed out?](#why-is-the-service-i-want-to-use-greyed-out) -* [I want to generate my own JWT. How do I do that?](#i-want-to-generate-my-own-jwt-how-do-i-do-that) -* [How do I know if I should use JWT or OAuth?](#how-do-i-know-if-i-should-use-jwt-or-oauth) * [Why can't I add Adobe I/O Runtime to my project?](#why-cant-i-add-adobe-io-runtime-to-my-project) * [How do I delete Runtime from my project or workspace?](#how-do-i-delete-runtime-from-my-project-or-workspace) @@ -50,7 +45,6 @@ This document provides answers to frequently asked questions about Adobe Develop ### Authentication * [Where can I find the sample code for OAuth authentication?](#where-can-i-find-the-sample-code-for-oauth-authentication) -* [How to use the private key for generating a JWT?](#how-to-use-the-private-key-for-generating-a-jwt) * [What do Default Redirect URI and the Redirect URI Pattern mean?](#what-do-default-redirect-uri-and-the-redirect-uri-pattern-mean) @@ -102,10 +96,6 @@ Quickly navigate between personal projects and your organization’s projects th Adobe I/O Runtime is only available for enterprise customers and requires a license. Please contact your Adobe sales representative for more details. -### I want to generate my own JWT. How do I do that? - -Head to Service Account (JWT) in the Credentials section to see your credential details and generate the JWT. - ### Why do I see a Read Only label on some of my projects? Even some that I created? A project or workspace is set to *Read Only* if you have not been granted access to all services within the project or workspace. Work with your organization's administrators to determine which services and product profiles you should have access to. @@ -114,10 +104,6 @@ A project or workspace is set to *Read Only* if you have not been granted access Certain Adobe products may need to create projects in Developer Console inside your organization. These auto-generated projects are visible, but cannot be edited, by normal organization users. -### How do I know if I should use JWT or OAuth? - -The authentication method depends on the type of app you're building. To learn more about authentication and authorization, read the [authentication documentation](../guides/authentication/index.md). - ### Why can't I change the name of my App Builder app? The app name is used to generate the URL for your project. We also leverage the app name for the namespace of each workspace. @@ -130,10 +116,6 @@ Integrations and plugins are now projects. Go to **Projects** in the UI to find There are currently code samples available for [NodeJS](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-auth-node) and [Python](https://github.com/AdobeDocs/adobe-dev-console/tree/main/samples/adobe-auth-python). -### How to use the private key for generating a JWT? - -Please copy the full content of private key including `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` to generate a correct JWT token. - ### What do Default Redirect URI and the Redirect URI Pattern mean? The default redirect URI is the URL where Adobe Identity Management Service (IMS) will send the authorization code after a successful login with Adobe. You will need that code to make a call to the token endpoint to receive an access token.