Skip to content

docs: add PAT and organization flow equivalency documentation #1187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
var config_conventional_1 = require("@commitlint/config-conventional");
var configs = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', __spreadArray(__spreadArray([], config_conventional_1.default.rules['type-enum'][2], true), ['blog'], false)],
},
};
exports.default = configs;
10 changes: 10 additions & 0 deletions docs/authorization/fragments/AuthorizationRequestExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use strict";

Check failure on line 1 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Do not use "use strict" directive

Check failure on line 1 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Replace `"use·strict"` with `'use·strict'`
Object.defineProperty(exports, "__esModule", { value: true });

Check failure on line 2 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Replace `"__esModule"` with `'__esModule'`

Check failure on line 2 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Do not use "exports"

Check failure on line 2 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

The use of method `Object.defineProperty` is not allowed as it will mutate its arguments
var CodeBlock_1 = require("@theme/CodeBlock");

Check failure on line 3 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Replace `"@theme/CodeBlock"` with `'@theme/CodeBlock'`

Check failure on line 3 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Do not use "require"

Check failure on line 3 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Identifier 'CodeBlock_1' is not in camel case

Check failure on line 3 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Expected 1 empty line after require statement not followed by another require

Check failure on line 3 in docs/authorization/fragments/AuthorizationRequestExample.js

View workflow job for this annotation

GitHub Actions / main-lint

Unexpected var, use let or const instead
var AuthorizationRequestExample = function (_a) {
var resource = _a.resource, scope = _a.scope;
return (<CodeBlock_1.default language="http">
{"GET /oidc/auth?response_type=code\n&client_id=your-client-id\n&redirect_uri=https://your-app.com/callback\n&scope=openid profile offline_access ".concat(scope, "\n&resource=").concat(resource, "\n&code_challenge=abc123\n&code_challenge_method=S256\n&state=xyz\nHTTP/1.1\nHost: your.logto.endpoint\n")}
</CodeBlock_1.default>);
};
exports.default = AuthorizationRequestExample;
13 changes: 13 additions & 0 deletions docs/authorization/fragments/ClientCredentialsRequestExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var CodeBlock_1 = require("@theme/CodeBlock");
var ClientCredentialsRequestExample = function (_a) {
var resource = _a.resource, organizationId = _a.organizationId, scope = _a.scope;
return (<CodeBlock_1.default language="http">
{"POST /oidc/token HTTP/1.1\nHost: your.logto.endpoint\nContent-Type: application/x-www-form-urlencoded\nAuthorization: Basic base64(client_id:client_secret)\n\ngrant_type=client_credentials\n"}
{resource && "&resource=".concat(resource, "\n")}
{organizationId && "&organization_id=".concat(organizationId, "\n")}
{"&scope=".concat(scope, "\n")}
</CodeBlock_1.default>);
};
exports.default = ClientCredentialsRequestExample;
18 changes: 18 additions & 0 deletions docs/authorization/fragments/TokenRequestExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var CodeBlock_1 = require("@theme/CodeBlock");
var TokenRequestExample = function (_a) {
var grantType = _a.grantType, resource = _a.resource, organizationId = _a.organizationId;
var params = [
"grant_type=".concat(grantType),
grantType === 'authorization_code' && 'code=authorization-code-received',
grantType === 'authorization_code' && 'redirect_uri=https://your-app.com/callback',
grantType === 'refresh_token' && 'refresh_token=your-refresh-token',
resource && "resource=".concat(resource),
organizationId && "organization_id=".concat(organizationId),
].filter(Boolean);
return (<CodeBlock_1.default language="http">
{"POST /oidc/token HTTP/1.1\nHost: your.logto.endpoint\nContent-Type: application/x-www-form-urlencoded\nAuthorization: Basic base64(client_id:client_secret)\n\n".concat(params.join('\n&'))}
</CodeBlock_1.default>);
};
exports.default = TokenRequestExample;
23 changes: 23 additions & 0 deletions docs/user-management/personal-access-token.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ After setting up the [Management API](/integrate-logto/interact-with-management-

After creating a PAT, you can use it to grant access tokens to your application by using the token exchange endpoint.

:::tip Token flow equivalency

Access tokens obtained using PATs work **identically** to tokens obtained through the standard `refresh_token` flow. This means:

- **Organization context**: PAT-obtained tokens support the same organization permissions and scopes as refresh token flows
- **Authorization flow**: You can use PAT-exchanged access tokens for [organization permissions](/authorization/organization-permissions) and [organization-level API resources](/authorization/organization-level-api-resources)
- **Token validation**: The same validation logic applies - only the initial grant type differs

If you're working with organizations, the access patterns and permissions are the same regardless of whether you use PAT or refresh tokens.

:::

### Request \{#request}

The application makes a [token exchange request](https://auth.wiki/authorization-code-flow#token-exchange-request) to the tenant's [token endpoint](/integrate-logto/application-data-structure#token-endpoint) with a special grant type using the HTTP POST method. The following parameters are included in the HTTP request entity-body using the `application/x-www-form-urlencoded` format.
Expand Down Expand Up @@ -84,6 +96,17 @@ The example access token payload:

## Related resources \{#related-resources}

### Organization support \{#organization-support}

PAT-exchanged access tokens fully support organization contexts and permissions. For organization-specific use cases:

- **[Organization permissions](/authorization/organization-permissions)**: Use PAT-exchanged access tokens to access organization-specific features and workflows
- **[Organization-level API resources](/authorization/organization-level-api-resources)**: Use PAT-exchanged access tokens to call organization-scoped APIs with proper permissions

The token exchange flow works the same way as refresh tokens for organization contexts - simply include the `organization_id` parameter when requesting tokens.

### Additional resources \{#additional-resources}

<Url href="https://blog.logto.io/api-authentication-with-personal-access-token">
What is personal access token? When should I use personal access tokens?
</Url>
Expand Down
295 changes: 295 additions & 0 deletions docusaurus-common.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.classicPresetConfig = exports.commonStylesheets = exports.commonMarkdown = exports.localeConfigs = exports.createCommonThemeConfig = exports.injectHeadTagsPlugin = exports.addAliasPlugin = exports.dracula = exports.getCloudflareSubdomain = exports.siteUrls = exports.isCfPagesPreview = exports.cfPagesBranch = exports.tutorialsSiteUrl = exports.mainSiteUrl = exports.__dirname = exports.localePath = exports.currentLocale = exports.defaultLocale = void 0;
var node_path_1 = require("node:path");
var node_url_1 = require("node:url");
var prism_react_renderer_1 = require("prism-react-renderer");
var rehype_katex_1 = require("rehype-katex");
var remark_math_1 = require("remark-math");
var utils_1 = require("./src/theme/BlogPostItem/utils");
exports.defaultLocale = 'en';
// A workaround for locale-specific values in the config
// https://github.com/facebook/docusaurus/issues/4542#issuecomment-1434839071
exports.currentLocale = String((_a = process.env.DOCUSAURUS_CURRENT_LOCALE) !== null && _a !== void 0 ? _a : exports.defaultLocale);
exports.localePath = exports.currentLocale === exports.defaultLocale ? '' : exports.currentLocale;
exports.__dirname = node_path_1.default.dirname((0, node_url_1.fileURLToPath)(import.meta.url));
exports.mainSiteUrl = 'https://docs.logto.io/';
exports.tutorialsSiteUrl = 'https://tutorials.logto.io/';
exports.cfPagesBranch = String(process.env.CF_PAGES_BRANCH);
exports.isCfPagesPreview = Boolean(exports.cfPagesBranch && exports.cfPagesBranch !== 'master');
exports.siteUrls = Object.freeze({
main: exports.mainSiteUrl,
tutorials: exports.tutorialsSiteUrl,
});
// https://community.cloudflare.com/t/algorithm-to-generate-a-preview-dns-subdomain-from-a-branch-name/477633/2
var getCloudflareSubdomain = function (branchName) {
return branchName
.replaceAll(/[^\da-z-]/g, '-')
.slice(0, 28)
.replace(/^-|-$/, '');
};
exports.getCloudflareSubdomain = getCloudflareSubdomain;
exports.dracula = prism_react_renderer_1.themes.dracula;
var addAliasPlugin = function () { return ({
name: 'add-alias-plugin',
configureWebpack: function () { return ({
resolve: {
alias: {
'@components': node_path_1.default.resolve(exports.__dirname, './src/components'),
'@mdx-components': node_path_1.default.resolve(exports.__dirname, './src/mdx-components'),
'@scss': node_path_1.default.resolve(exports.__dirname, './src/scss'),
},
},
}); },
}); };
exports.addAliasPlugin = addAliasPlugin;
var injectHeadTagsPlugin = function () { return ({
name: 'inject-head-tags-plugin',
injectHtmlTags: function () { return ({
headTags: [
{
tagName: 'script',
innerHTML: "\n var shouldTrack =\n window.location.hostname === 'logto.io' || window.location.hostname.endsWith('logto.io');\n if (!shouldTrack) {\n console.warn('Not tracking because the hostname is not logto.io');\n }\n ",
},
{
tagName: 'script',
attributes: {
src: 'https://akasha.logto.io/placebo/sabaean.js',
defer: true,
'data-api': 'https://akasha.logto.io/placebo/eagan',
'data-domain': 'logto.io',
},
},
{
tagName: 'script',
innerHTML: "\n if (shouldTrack) {\n window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) };\n }\n ",
},
{
tagName: 'meta',
attributes: {
name: 'google-site-verification',
content: '3EYzsnarDwG6zL2dlHvyC8ySVcV6Q3RGlvh7-bvhb2k',
},
},
],
}); },
}); };
exports.injectHeadTagsPlugin = injectHeadTagsPlugin;
/**
* Create a common theme config for a Docusaurus site.
*
* If `isMainSite` is true, the config will generate relative URLs when possible; otherwise, it will
* generate absolute URLs. This is useful when deploying multiple Docusaurus sites under the same
* domain.
*
* For example, the main site is deployed at `https://docs.logto.io/`, and the
* tutorials site is deployed at `https://tutorials.logto.io/`. A relative URL for the main site
* in the tutorials site would result 404 errors if the URL is not prefixed with the main site
* domain, as each site is a standalone single-page application.
*/
var createCommonThemeConfig = function (site) {
var buildUrl = function (pathname, forSite) {
return site === forSite ? pathname : new URL(pathname, exports.siteUrls[forSite]).href;
};
return Object.freeze({
navbar: {
logo: {
alt: 'Logto Logo',
src: 'img/logto.svg',
srcDark: 'img/logto_dark.svg',
href: new URL(exports.localePath, 'https://logto.io/').href,
},
items: [
{
to: buildUrl('/introduction', 'main'),
position: 'left',
label: 'Docs',
},
{
to: buildUrl('/quick-starts', 'main'),
position: 'left',
label: 'Quick starts',
},
{
to: buildUrl('/integrations', 'main'),
position: 'left',
label: 'Integrations',
},
{
to: buildUrl('/api-protection', 'main'),
position: 'left',
label: 'API protection',
},
{
to: buildUrl('/use-cases', 'main'),
position: 'left',
label: 'Use cases',
},
{
to: new URL('https://openapi.logto.io', exports.mainSiteUrl).href,
position: 'left',
label: 'Logto APIs',
},
{
type: 'localeDropdown',
position: 'right',
},
],
},
footer: {
logo: {
alt: 'Logo',
src: '/img/silverhand.svg',
},
style: 'dark',
links: [
{
title: 'Developers',
items: [
{ label: 'Docs', to: buildUrl('/introduction', 'main') },
{ label: 'Quick starts', to: buildUrl('/quick-starts', 'main') },
{ label: 'Integrations', to: buildUrl('/integrations', 'main') },
{
label: 'Account API',
href: 'https://openapi.logto.io/group/endpoint-account-center',
},
{
label: 'Experience API',
href: 'https://openapi.logto.io/group/endpoint-experience',
},
{ label: 'Management API', href: 'https://openapi.logto.io' },
{ label: 'Build X with Y', to: buildUrl('/' + utils_1.howToBasePath, 'tutorials') },
],
},
{
title: 'Resources',
items: [
{ label: 'Pricing', href: 'https://logto.io/pricing' },
{ label: 'Blogs', href: 'https://blog.logto.io' },
{ label: 'Auth Wiki', href: 'https://auth.wiki' },
{ label: "What's new", href: 'https://blog.logto.io/categories/changelogs/#all' },
{ label: 'YouTube', href: 'https://youtube.com/@logto-io' },
{ label: 'GitHub', href: 'https://github.com/logto-io/logto' },
],
},
{
title: 'Need help?',
items: [
{
label: 'Contact support',
href: 'https://logto.io/contact',
icon: 'email',
hideExternalLinkIcon: true,
},
{
label: 'Open a GitHub issue',
href: 'https://github.com/logto-io/logto/issues/new/choose',
icon: 'github',
hideExternalLinkIcon: true,
},
{
label: 'Request a new feature',
href: 'https://logto.productlane.com/roadmap',
icon: 'roadmap',
hideExternalLinkIcon: true,
},
{
label: 'Ask the Discord community',
href: 'https://discord.com/invite/UEPaF3j5e6',
icon: 'discord',
hideExternalLinkIcon: true,
},
],
},
],
copyright: "Designed by Silverhand Inc.",
},
prism: {
theme: exports.dracula,
darkTheme: exports.dracula,
additionalLanguages: [
'bash',
'csharp',
'cshtml',
'dart',
'erb',
'groovy',
'http',
'java',
'json',
'json5',
'kotlin',
'php',
'ruby',
'swift',
'toml',
],
},
colorMode: {
respectPrefersColorScheme: true,
},
algolia: {
appId: 'DE7QZWOVO6',
apiKey: '4c64ad7f3b8622f59d8121dbac801337',
indexName: 'logto',
},
});
};
exports.createCommonThemeConfig = createCommonThemeConfig;
exports.localeConfigs = Object.freeze({
'zh-CN': { label: '简体中文' },
'zh-TW': { label: '繁體中文(台灣)' },
});
exports.commonMarkdown = {
mermaid: true,
};
exports.commonStylesheets = [
{
href: 'https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css',
type: 'text/css',
crossorigin: 'anonymous',
},
];
exports.classicPresetConfig = {
sitemap: {
changefreq: 'weekly',
ignorePatterns: [
'/blog/**',
// Some pages are not translated. Ignore them.
'/*/terms',
'/*/terms/**',
'/*/about',
'/*/about/**',
],
},
docs: {
routeBasePath: '/',
breadcrumbs: true,
sidebarPath: './src/sidebar/index.ts',
editUrl: 'https://github.com/logto-io/docs/tree/master',
editLocalizedFiles: true,
// To enabled math formula rendering
// See https://docusaurus.io/docs/markdown-features/math-equations#configuration
remarkPlugins: [remark_math_1.default],
rehypePlugins: [rehype_katex_1.default],
},
theme: {
customCss: './src/scss/custom.scss',
},
svgr: {
svgrConfig: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: { removeViewBox: false },
},
},
'prefixIds',
],
},
},
},
};
Loading
Loading