CINXE.COM

OAuth - AT Protocol

<!DOCTYPE html><html lang="en" class="h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/786ca9324488b5df.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-496a37b6c26ccd6a.js"/><script src="/_next/static/chunks/fd9d1056-ae1d3d1d803e2e5a.js" async=""></script><script src="/_next/static/chunks/117-545f1187d7f142b2.js" async=""></script><script src="/_next/static/chunks/main-app-3122dc718821276f.js" async=""></script><script src="/_next/static/chunks/972-f3553dcbba031b91.js" async=""></script><script src="/_next/static/chunks/135-8f28b0a70a96c388.js" async=""></script><script src="/_next/static/chunks/212-67e821188dcd5fb8.js" async=""></script><script src="/_next/static/chunks/app/%5Blocale%5D/specs/oauth/page-83f2d22457027977.js" async=""></script><script src="/_next/static/chunks/28-313384c6289a00c0.js" async=""></script><script src="/_next/static/chunks/369-a583bbb81b96a7d8.js" async=""></script><script src="/_next/static/chunks/60-3e1824554a9cf3f8.js" async=""></script><script src="/_next/static/chunks/app/%5Blocale%5D/layout-8706067c5d993875.js" async=""></script><title>OAuth - AT Protocol</title><meta name="description" content="OAuth for Client/Server Authentication and Authorization"/><meta property="og:title" content="OAuth - AT Protocol"/><meta property="og:description" content="OAuth for Client/Server Authentication and Authorization"/><meta property="og:url" content="https://atproto.com/"/><meta property="og:site_name" content="AT Protocol"/><meta property="og:image" content="https://atproto.com/default-social-card.png"/><meta property="og:image:secure_url" content="https://atproto.com/default-social-card.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:type" content="website"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:title" content="OAuth - AT Protocol"/><meta name="twitter:description" content="OAuth for Client/Server Authentication and Authorization"/><meta name="twitter:image" content="https://atproto.com/default-social-card.png"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="flex min-h-full bg-white antialiased dark:bg-zinc-900"><script>!function(){try{var d=document.documentElement,c=d.classList;c.remove('light','dark');var e=localStorage.getItem('theme');if('system'===e||(!e&&true)){var t='(prefers-color-scheme: dark)',m=window.matchMedia(t);if(m.media!==t||m.matches){d.style.colorScheme = 'dark';c.add('dark')}else{d.style.colorScheme = 'light';c.add('light')}}else if(e){c.add(e|| '')}if(e==='light'||e==='dark')d.style.colorScheme=e}catch(e){}}()</script><div class="w-full"><div class="h-full lg:ml-72 xl:ml-80"><header class="contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex"><div class="contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pb-8 lg:pt-4 xl:w-80 lg:dark:border-white/10"><div class="hidden lg:flex"><a aria-label="Home" href="/"><svg viewBox="0 0 162 28" aria-hidden="true" class="h-6"><path class="fill-blue-500" d="M14.362 27.78c-1.956 0-3.756-.324-5.4-.972-1.644-.648-3.072-1.566-4.284-2.754a12.247 12.247 0 0 1-2.808-4.158c-.66-1.596-.99-3.33-.99-5.202 0-2.232.342-4.212 1.026-5.94.696-1.728 1.656-3.18 2.88-4.356a12.48 12.48 0 0 1 4.284-2.7c1.632-.612 3.378-.918 5.238-.918 2.28 0 4.278.354 5.994 1.062 1.716.708 3.144 1.668 4.284 2.88a11.706 11.706 0 0 1 2.538 4.158c.552 1.548.804 3.168.756 4.86-.06 2.328-.546 4.116-1.458 5.364-.912 1.236-2.328 1.854-4.248 1.854a5.839 5.839 0 0 1-2.826-.702 3.703 3.703 0 0 1-1.764-2.07l1.044.054c-.492.924-1.164 1.572-2.016 1.944a6.464 6.464 0 0 1-2.61.558c-1.212 0-2.28-.258-3.204-.774a5.682 5.682 0 0 1-2.178-2.214c-.528-.948-.792-2.046-.792-3.294 0-1.284.276-2.394.828-3.33a5.77 5.77 0 0 1 2.232-2.196c.936-.516 1.992-.774 3.168-.774.78 0 1.59.162 2.43.486.852.324 1.512.78 1.98 1.368l-.738.936V8.664h2.412l-.054 6.462c0 .924.18 1.62.54 2.088.36.468.894.702 1.602.702.624 0 1.104-.174 1.44-.522.348-.36.588-.846.72-1.458a10.66 10.66 0 0 0 .252-2.106c.036-1.86-.24-3.426-.828-4.698-.588-1.272-1.386-2.298-2.394-3.078a9.499 9.499 0 0 0-3.294-1.71c-1.2-.36-2.394-.54-3.582-.54-1.68 0-3.174.27-4.482.81-1.308.528-2.412 1.278-3.312 2.25-.888.96-1.56 2.1-2.016 3.42-.444 1.308-.654 2.748-.63 4.32.048 1.56.33 2.964.846 4.212a9.324 9.324 0 0 0 2.16 3.204 9.38 9.38 0 0 0 3.276 2.034c1.26.468 2.64.702 4.14.702.84 0 1.674-.096 2.502-.288.84-.18 1.608-.438 2.304-.774l1.026 2.808c-.924.432-1.896.75-2.916.954a14.649 14.649 0 0 1-3.078.324Zm-.144-10.098c.852 0 1.566-.246 2.142-.738.576-.492.864-1.326.864-2.502 0-1.068-.258-1.872-.774-2.412-.504-.552-1.218-.828-2.142-.828-1.092 0-1.908.288-2.448.864-.54.576-.81 1.368-.81 2.376 0 1.032.276 1.83.828 2.394.564.564 1.344.846 2.34.846Z"></path><path class="fill-blue-600 dark:fill-blue-500" d="M51.799 7.813V5.545h13.509v2.268H59.86V23h-2.624V7.812h-5.438ZM39.392 23h-2.795l6.28-17.455h3.043L52.203 23h-2.796L44.472 8.716h-.137L39.392 23Zm.469-6.835h9.068v2.216h-9.068v-2.216Z"></path><path class="fill-zinc-700 dark:fill-zinc-400" d="M161.144 5.545V23h-2.548V5.545h2.548ZM149.649 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.131-2.361-.505-1.012-.758-2.194-.758-3.546 0-1.358.253-2.545.758-3.562.506-1.017 1.216-1.807 2.131-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.506 1.017.758 2.204.758 3.562 0 1.352-.252 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.009-2.139c.795 0 1.454-.21 1.977-.63.523-.421.909-.98 1.159-1.68.256-.699.384-1.468.384-2.31 0-.834-.128-1.602-.384-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.977-.639-.801 0-1.466.213-1.995.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.645 1.258 1.167 1.678.529.42 1.194.631 1.995.631ZM136.032 23.264c-1.267 0-2.358-.287-3.273-.86-.909-.58-1.608-1.378-2.096-2.395-.489-1.018-.733-2.182-.733-3.495 0-1.33.25-2.503.75-3.52.5-1.022 1.204-1.82 2.113-2.395.909-.573 1.98-.86 3.213-.86.995 0 1.881.184 2.659.554a4.764 4.764 0 0 1 1.884 1.534c.483.659.77 1.429.861 2.31h-2.48a2.974 2.974 0 0 0-.938-1.586c-.483-.443-1.13-.665-1.943-.665-.71 0-1.332.188-1.866.563-.529.37-.941.898-1.236 1.585-.296.682-.443 1.489-.443 2.42 0 .955.144 1.779.434 2.472.29.693.699 1.23 1.227 1.61.535.382 1.162.572 1.884.572.483 0 .92-.088 1.313-.264.397-.182.73-.44.997-.776.272-.335.463-.739.571-1.21h2.48a4.823 4.823 0 0 1-.827 2.267 4.76 4.76 0 0 1-1.849 1.568c-.767.38-1.668.571-2.702.571ZM121.571 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.131-2.361-.505-1.012-.758-2.194-.758-3.546 0-1.358.253-2.545.758-3.562.506-1.017 1.216-1.807 2.131-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.505 1.017.758 2.204.758 3.562 0 1.352-.253 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.009-2.139c.795 0 1.454-.21 1.977-.63.523-.421.909-.98 1.159-1.68.256-.699.383-1.468.383-2.31 0-.834-.127-1.602-.383-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.977-.639-.802 0-1.466.213-1.995.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.645 1.258 1.167 1.678.529.42 1.193.631 1.995.631ZM113.379 9.91v2.044h-7.151V9.91h7.151Zm-5.233-3.137h2.548v12.383c0 .495.074.867.221 1.117.148.244.339.412.572.503.238.085.497.127.775.127.205 0 .384-.014.537-.042l.358-.068.46 2.105a4.307 4.307 0 0 1-.63.17 4.992 4.992 0 0 1-1.023.102 4.483 4.483 0 0 1-1.875-.358 3.208 3.208 0 0 1-1.406-1.159c-.358-.522-.537-1.179-.537-1.968V6.773ZM98.321 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.13-2.361-.506-1.012-.76-2.194-.76-3.546 0-1.358.254-2.545.76-3.562.505-1.017 1.215-1.807 2.13-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.505 1.017.758 2.204.758 3.562 0 1.352-.253 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.008-2.139c.796 0 1.455-.21 1.978-.63.523-.421.909-.98 1.159-1.68.256-.699.383-1.468.383-2.31 0-.834-.127-1.602-.383-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.978-.639-.8 0-1.465.213-1.994.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.644 1.258 1.167 1.678.529.42 1.193.631 1.995.631ZM84.065 23V9.91h2.463v2.079h.136a3.164 3.164 0 0 1 1.261-1.662 3.61 3.61 0 0 1 2.063-.614 10.896 10.896 0 0 1 1.082.06v2.437a4.577 4.577 0 0 0-.545-.094 5.202 5.202 0 0 0-.784-.06c-.603 0-1.14.129-1.611.384a2.85 2.85 0 0 0-1.517 2.566V23h-2.548ZM68.918 23V5.545h6.221c1.358 0 2.483.248 3.375.742.892.494 1.56 1.17 2.003 2.028.443.853.665 1.813.665 2.881 0 1.074-.225 2.04-.674 2.898-.443.852-1.113 1.528-2.01 2.028-.893.494-2.015.742-3.367.742h-4.279V14.63h4.04c.858 0 1.554-.148 2.088-.444.534-.3.926-.71 1.176-1.227.25-.517.375-1.105.375-1.764 0-.66-.125-1.244-.375-1.756-.25-.511-.645-.912-1.184-1.201-.534-.29-1.239-.435-2.114-.435h-3.307V23h-2.633Z"></path></svg></a></div><div class="fixed inset-x-0 top-0 z-50 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6 lg:left-72 lg:z-30 lg:px-8 xl:left-80 backdrop-blur-sm lg:left-72 xl:left-80 dark:backdrop-blur bg-white/[var(--bg-opacity-light)] dark:bg-zinc-900/[var(--bg-opacity-dark)]" style="--bg-opacity-light:0.5;--bg-opacity-dark:0.2"><div class="absolute inset-x-0 top-full h-px transition bg-zinc-900/7.5 dark:bg-white/7.5"></div><div class="hidden lg:block lg:max-w-md lg:flex-auto"><button type="button" class="hidden h-8 w-full items-center gap-2 rounded-full bg-white pl-2 pr-3 text-sm text-zinc-500 ring-1 ring-zinc-900/10 transition hover:ring-zinc-900/20 ui-not-focus-visible:outline-none lg:flex dark:bg-white/5 dark:text-zinc-400 dark:ring-inset dark:ring-white/10 dark:hover:ring-white/20"><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"></path></svg>Find something...<kbd class="ml-auto text-2xs text-zinc-400 dark:text-zinc-500"><kbd class="font-sans"></kbd><kbd class="font-sans">K</kbd></kbd></button><!--$--><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span><!--/$--></div><div class="flex items-center gap-5 lg:hidden"><button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5" aria-label="Toggle navigation"><svg viewBox="0 0 10 9" fill="none" stroke-linecap="round" aria-hidden="true" class="w-2.5 stroke-zinc-900 dark:stroke-white"><path d="M.5 1h9M.5 8h9M.5 4.5h9"></path></svg></button><!--$--><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span><!--/$--><a aria-label="Home" href="/"><svg viewBox="0 0 162 28" aria-hidden="true" class="h-6"><path class="fill-blue-500" d="M14.362 27.78c-1.956 0-3.756-.324-5.4-.972-1.644-.648-3.072-1.566-4.284-2.754a12.247 12.247 0 0 1-2.808-4.158c-.66-1.596-.99-3.33-.99-5.202 0-2.232.342-4.212 1.026-5.94.696-1.728 1.656-3.18 2.88-4.356a12.48 12.48 0 0 1 4.284-2.7c1.632-.612 3.378-.918 5.238-.918 2.28 0 4.278.354 5.994 1.062 1.716.708 3.144 1.668 4.284 2.88a11.706 11.706 0 0 1 2.538 4.158c.552 1.548.804 3.168.756 4.86-.06 2.328-.546 4.116-1.458 5.364-.912 1.236-2.328 1.854-4.248 1.854a5.839 5.839 0 0 1-2.826-.702 3.703 3.703 0 0 1-1.764-2.07l1.044.054c-.492.924-1.164 1.572-2.016 1.944a6.464 6.464 0 0 1-2.61.558c-1.212 0-2.28-.258-3.204-.774a5.682 5.682 0 0 1-2.178-2.214c-.528-.948-.792-2.046-.792-3.294 0-1.284.276-2.394.828-3.33a5.77 5.77 0 0 1 2.232-2.196c.936-.516 1.992-.774 3.168-.774.78 0 1.59.162 2.43.486.852.324 1.512.78 1.98 1.368l-.738.936V8.664h2.412l-.054 6.462c0 .924.18 1.62.54 2.088.36.468.894.702 1.602.702.624 0 1.104-.174 1.44-.522.348-.36.588-.846.72-1.458a10.66 10.66 0 0 0 .252-2.106c.036-1.86-.24-3.426-.828-4.698-.588-1.272-1.386-2.298-2.394-3.078a9.499 9.499 0 0 0-3.294-1.71c-1.2-.36-2.394-.54-3.582-.54-1.68 0-3.174.27-4.482.81-1.308.528-2.412 1.278-3.312 2.25-.888.96-1.56 2.1-2.016 3.42-.444 1.308-.654 2.748-.63 4.32.048 1.56.33 2.964.846 4.212a9.324 9.324 0 0 0 2.16 3.204 9.38 9.38 0 0 0 3.276 2.034c1.26.468 2.64.702 4.14.702.84 0 1.674-.096 2.502-.288.84-.18 1.608-.438 2.304-.774l1.026 2.808c-.924.432-1.896.75-2.916.954a14.649 14.649 0 0 1-3.078.324Zm-.144-10.098c.852 0 1.566-.246 2.142-.738.576-.492.864-1.326.864-2.502 0-1.068-.258-1.872-.774-2.412-.504-.552-1.218-.828-2.142-.828-1.092 0-1.908.288-2.448.864-.54.576-.81 1.368-.81 2.376 0 1.032.276 1.83.828 2.394.564.564 1.344.846 2.34.846Z"></path><path class="fill-blue-600 dark:fill-blue-500" d="M51.799 7.813V5.545h13.509v2.268H59.86V23h-2.624V7.812h-5.438ZM39.392 23h-2.795l6.28-17.455h3.043L52.203 23h-2.796L44.472 8.716h-.137L39.392 23Zm.469-6.835h9.068v2.216h-9.068v-2.216Z"></path><path class="fill-zinc-700 dark:fill-zinc-400" d="M161.144 5.545V23h-2.548V5.545h2.548ZM149.649 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.131-2.361-.505-1.012-.758-2.194-.758-3.546 0-1.358.253-2.545.758-3.562.506-1.017 1.216-1.807 2.131-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.506 1.017.758 2.204.758 3.562 0 1.352-.252 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.009-2.139c.795 0 1.454-.21 1.977-.63.523-.421.909-.98 1.159-1.68.256-.699.384-1.468.384-2.31 0-.834-.128-1.602-.384-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.977-.639-.801 0-1.466.213-1.995.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.645 1.258 1.167 1.678.529.42 1.194.631 1.995.631ZM136.032 23.264c-1.267 0-2.358-.287-3.273-.86-.909-.58-1.608-1.378-2.096-2.395-.489-1.018-.733-2.182-.733-3.495 0-1.33.25-2.503.75-3.52.5-1.022 1.204-1.82 2.113-2.395.909-.573 1.98-.86 3.213-.86.995 0 1.881.184 2.659.554a4.764 4.764 0 0 1 1.884 1.534c.483.659.77 1.429.861 2.31h-2.48a2.974 2.974 0 0 0-.938-1.586c-.483-.443-1.13-.665-1.943-.665-.71 0-1.332.188-1.866.563-.529.37-.941.898-1.236 1.585-.296.682-.443 1.489-.443 2.42 0 .955.144 1.779.434 2.472.29.693.699 1.23 1.227 1.61.535.382 1.162.572 1.884.572.483 0 .92-.088 1.313-.264.397-.182.73-.44.997-.776.272-.335.463-.739.571-1.21h2.48a4.823 4.823 0 0 1-.827 2.267 4.76 4.76 0 0 1-1.849 1.568c-.767.38-1.668.571-2.702.571ZM121.571 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.131-2.361-.505-1.012-.758-2.194-.758-3.546 0-1.358.253-2.545.758-3.562.506-1.017 1.216-1.807 2.131-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.505 1.017.758 2.204.758 3.562 0 1.352-.253 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.009-2.139c.795 0 1.454-.21 1.977-.63.523-.421.909-.98 1.159-1.68.256-.699.383-1.468.383-2.31 0-.834-.127-1.602-.383-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.977-.639-.802 0-1.466.213-1.995.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.645 1.258 1.167 1.678.529.42 1.193.631 1.995.631ZM113.379 9.91v2.044h-7.151V9.91h7.151Zm-5.233-3.137h2.548v12.383c0 .495.074.867.221 1.117.148.244.339.412.572.503.238.085.497.127.775.127.205 0 .384-.014.537-.042l.358-.068.46 2.105a4.307 4.307 0 0 1-.63.17 4.992 4.992 0 0 1-1.023.102 4.483 4.483 0 0 1-1.875-.358 3.208 3.208 0 0 1-1.406-1.159c-.358-.522-.537-1.179-.537-1.968V6.773ZM98.321 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.13-2.361-.506-1.012-.76-2.194-.76-3.546 0-1.358.254-2.545.76-3.562.505-1.017 1.215-1.807 2.13-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.505 1.017.758 2.204.758 3.562 0 1.352-.253 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.008-2.139c.796 0 1.455-.21 1.978-.63.523-.421.909-.98 1.159-1.68.256-.699.383-1.468.383-2.31 0-.834-.127-1.602-.383-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.978-.639-.8 0-1.465.213-1.994.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.644 1.258 1.167 1.678.529.42 1.193.631 1.995.631ZM84.065 23V9.91h2.463v2.079h.136a3.164 3.164 0 0 1 1.261-1.662 3.61 3.61 0 0 1 2.063-.614 10.896 10.896 0 0 1 1.082.06v2.437a4.577 4.577 0 0 0-.545-.094 5.202 5.202 0 0 0-.784-.06c-.603 0-1.14.129-1.611.384a2.85 2.85 0 0 0-1.517 2.566V23h-2.548ZM68.918 23V5.545h6.221c1.358 0 2.483.248 3.375.742.892.494 1.56 1.17 2.003 2.028.443.853.665 1.813.665 2.881 0 1.074-.225 2.04-.674 2.898-.443.852-1.113 1.528-2.01 2.028-.893.494-2.015.742-3.367.742h-4.279V14.63h4.04c.858 0 1.554-.148 2.088-.444.534-.3.926-.71 1.176-1.227.25-.517.375-1.105.375-1.764 0-.66-.125-1.244-.375-1.756-.25-.511-.645-.912-1.184-1.201-.534-.29-1.239-.435-2.114-.435h-3.307V23h-2.633Z"></path></svg></a></div><div class="flex items-center gap-5"><nav class="hidden md:block"><ul role="list" class="flex items-center gap-8"><li><a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/sdks">SDKs</a></li><li><a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="https://docs.bsky.app/blog">Blog</a></li><li><a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="https://github.com/bluesky-social/atproto">GitHub</a></li><select class="block w-full appearance-none rounded-md border-0 py-1.5 pl-3 pr-3 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6 dark:bg-gray-800 dark:text-gray-100 dark:ring-gray-700"><option value="en" selected="">English</option><option value="pt">Português</option><option value="ja">日本語</option></select></ul></nav><div class="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15"></div><div class="flex gap-4"><div class="contents lg:hidden"><button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 ui-not-focus-visible:outline-none lg:hidden dark:hover:bg-white/5" aria-label="Find something..."><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-zinc-900 dark:stroke-white"><path stroke-linecap="round" stroke-linejoin="round" d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"></path></svg></button><!--$--><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span><!--/$--></div><button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5" aria-label="Toggle theme"><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-zinc-900 dark:hidden"><path d="M12.5 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Z"></path><path stroke-linecap="round" d="M10 5.5v-1M13.182 6.818l.707-.707M14.5 10h1M13.182 13.182l.707.707M10 15.5v-1M6.11 13.889l.708-.707M4.5 10h1M6.11 6.111l.708.707"></path></svg><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="hidden h-5 w-5 stroke-white dark:block"><path d="M15.224 11.724a5.5 5.5 0 0 1-6.949-6.949 5.5 5.5 0 1 0 6.949 6.949Z"></path></svg></button></div></div></div><nav class="hidden lg:mt-10 lg:block"><ul role="list"><li class="md:hidden"><a class="block py-1 text-sm text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/">API</a></li><li class="md:hidden"><a class="block py-1 text-sm text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="#">Documentation</a></li><li class="md:hidden"><a class="block py-1 text-sm text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="#">Support</a></li><li class="relative mt-6 md:mt-0"><h2 class="text-xs font-semibold text-zinc-900 dark:text-white">Home</h2><div class="relative mt-3 pl-2"><div class="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5"></div><ul role="list" class="border-l border-transparent"><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/"><span class="truncate">Introduction</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/sdks"><span class="truncate">SDKs</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/glossary"><span class="truncate">Glossary</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/faq"><span class="truncate">FAQ</span></a></li></ul></div></li><li class="relative mt-6"><h2 class="text-xs font-semibold text-zinc-900 dark:text-white">Building apps</h2><div class="relative mt-3 pl-2"><div class="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5"></div><ul role="list" class="border-l border-transparent"><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/applications"><span class="truncate">Quick start</span></a></li></ul></div></li><li class="relative mt-6"><h2 class="text-xs font-semibold text-zinc-900 dark:text-white">Guides</h2><div class="relative mt-3 pl-2"><div class="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5"></div><ul role="list" class="border-l border-transparent"><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/overview"><span class="truncate">Overview</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/self-hosting"><span class="truncate">Self-hosting</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/identity"><span class="truncate">Identity</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/data-repos"><span class="truncate">Data repos</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/lexicon"><span class="truncate">Schemas &amp; Lexicon</span></a></li></ul></div></li><li class="relative mt-6"><h2 class="text-xs font-semibold text-zinc-900 dark:text-white">Specs</h2><div class="relative mt-3 pl-2"><div class="absolute inset-x-0 top-0 bg-zinc-800/2.5 will-change-transform dark:bg-white/2.5" style="border-radius:8px;height:32px;top:256px;opacity:0"></div><div class="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5"></div><div class="absolute left-2 h-6 w-px bg-blue-500" style="top:260px;opacity:1"></div><ul role="list" class="border-l border-transparent"><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/atp"><span class="truncate">AT Protocol</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/data-model"><span class="truncate">Data Model</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/lexicon"><span class="truncate">Lexicon</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/cryptography"><span class="truncate">Cryptography</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/repository"><span class="truncate">Repository</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/blob"><span class="truncate">Blobs</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/label"><span class="truncate">Labels</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/xrpc"><span class="truncate">HTTP API (XRPC)</span></a></li><li class="relative"><a aria-current="page" class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-900 dark:text-white" href="/specs/oauth"><span class="truncate">OAuth</span></a><ul role="list" style="opacity:1"><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#clients"><span class="truncate">Clients</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#identity-authentication"><span class="truncate">Identity Authentication</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#authorization-scopes"><span class="truncate">Authorization Scopes</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#authorization-requests"><span class="truncate">Authorization Requests</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#tokens-and-session-lifetime"><span class="truncate">Tokens and Session Lifetime</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#demonstrating-proof-of-possession-d-po-p"><span class="truncate">Demonstrating Proof of Possession (DPoP)</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#authorization-servers"><span class="truncate">Authorization Servers</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#summary-of-authorization-flow"><span class="truncate">Summary of Authorization Flow</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#security-considerations"><span class="truncate">Security Considerations</span></a></li><li><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-7 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth#possible-future-changes"><span class="truncate">Possible Future Changes</span></a></li></ul></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/event-stream"><span class="truncate">Event Stream</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/did"><span class="truncate">DID</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/handle"><span class="truncate">Handle</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/nsid"><span class="truncate">NSID</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/record-key"><span class="truncate">Record Key</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/at-uri-scheme"><span class="truncate">URI Scheme</span></a></li></ul></div></li></ul></nav></div></header><div class="relative flex h-full flex-col px-4 pt-14 sm:px-6 lg:px-8"><main class="flex-auto"><article class="flex h-full flex-col pb-24 pt-16"><div class="flex-auto prose dark:prose-invert [html_:where(&amp;&gt;*)]:mx-auto [html_:where(&amp;&gt;*)]:max-w-2xl [html_:where(&amp;&gt;*)]:lg:mx-[calc(50%-min(50%,theme(maxWidth.lg)))] [html_:where(&amp;&gt;*)]:lg:max-w-3xl"><h1>OAuth</h1> <div class="my-6 flex gap-2.5 rounded-2xl border border-blue-500/20 bg-blue-50/50 p-4 leading-6 text-blue-900 dark:border-blue-500/30 dark:bg-blue-500/5 dark:text-blue-200 dark:[--tw-prose-links-hover:theme(colors.blue.300)] dark:[--tw-prose-links:theme(colors.white)]"><svg viewBox="0 0 16 16" aria-hidden="true" class="mt-1 h-4 w-4 flex-none fill-blue-500 stroke-white dark:fill-blue-200/20 dark:stroke-blue-200"><circle cx="8" cy="8" r="8" stroke-width="0"></circle><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6.75 7.75h1.5v3.5"></path><circle cx="8" cy="4" r=".5" fill="none"></circle></svg><div class="[&amp;&gt;:first-child]:mt-0 [&amp;&gt;:last-child]:mb-0"><p>The OAuth profile for atproto is new and may be revised based on feedback from the development community and ongoing standards work. Read more about the rollout in the <a href="https://github.com/bluesky-social/atproto/discussions/2656">OAuth Roadmap</a>.</p></div></div> <div class="my-6 flex gap-2.5 rounded-2xl border border-blue-500/20 bg-blue-50/50 p-4 leading-6 text-blue-900 dark:border-blue-500/30 dark:bg-blue-500/5 dark:text-blue-200 dark:[--tw-prose-links-hover:theme(colors.blue.300)] dark:[--tw-prose-links:theme(colors.white)]"><svg viewBox="0 0 16 16" aria-hidden="true" class="mt-1 h-4 w-4 flex-none fill-blue-500 stroke-white dark:fill-blue-200/20 dark:stroke-blue-200"><circle cx="8" cy="8" r="8" stroke-width="0"></circle><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6.75 7.75h1.5v3.5"></path><circle cx="8" cy="4" r=".5" fill="none"></circle></svg><div class="[&amp;&gt;:first-child]:mt-0 [&amp;&gt;:last-child]:mb-0"><p>This specification is authoritative, but is not an implementation guide and does not provide much background or context around design decisions. The earlier <a href="https://github.com/bluesky-social/proposals/tree/main/0004-oauth">design proposal</a> is not authoritative but provides more context and examples. SDK documentation and the <a href="https://docs.bsky.app/docs/advanced-guides/oauth-client">client implementation guide</a> are more approachable for developers.</p></div></div> <p>OAuth is the primary mechanism in atproto for clients to make authorized requests to PDS instances. Most user-facing software is expected to use OAuth, including &quot;front-end&quot; clients like mobile apps, rich browser apps, or native desktop apps, as well as &quot;back-end&quot; clients like web services.</p> <p>See the <a href="./xrpc">HTTP API specification</a> for other forms of auth in atproto, including legacy HTTP client sessions/tokens, and inter-service auth.</p> <p>OAuth is a constantly evolving framework of standards and best practices, standardized by the IETF. atproto uses a specific &quot;profile&quot; of OAuth which mandates a particular combination of OAuth standards, as described in this document.</p> <p>At a high level, we start with the &quot;OAuth 2.1&quot; (<a href="https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/"><code>draft-ietf-oauth-v2-1</code></a>) evolution of OAuth 2.0, which means:</p> <ul> <li>only the &quot;authorization code&quot; OAuth 2.0 grant type is supported, not &quot;implicit&quot; or other grant types</li> <li>mandatory Proof Key for Code Exchange (PKCE, <a href="https://datatracker.ietf.org/doc/html/rfc7636">RFC 7636</a>)</li> <li>security best practices (<a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics"><code>draft-ietf-oauth-security-topics</code></a> and <a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps"><code>draft-ietf-oauth-browser-based-apps</code></a>) are required</li> </ul> <p>Unlike a centralized app platform, in atproto there are many independent server implementations, so server discovery and client registration are automated using a combination of public auth server metadata and public client metadata. The <code>client_id</code> is a fully-qualified web URL pointing to the public client metadata (JSON document). There is no <code>client_secret</code> shared between servers and clients. When initiating a login with a handle or DID, an atproto-specific identity resolution step is required to discover the account’s PDS network location.</p> <p>In OAuth terminology, an atproto Personal Data Server (PDS) is a &quot;Resource Server&quot; to which authorized HTTP requests are made using access tokens. Sometimes the PDS is also the &quot;Authorization Server&quot; - which services OAuth authorization flows and token requests - while in other situations a separate &quot;entryway&quot; service acts as the Authorization Server for multiple PDS instances. Clients from a metadata file from the PDS to discover the Authorization Server network location.</p> <p>DPoP (with mandatory server issued nonces) is required to bind auth tokens to specific client software instances (eg, end devices or browser sessions). Pushed Authentication Requests (PAR) are used to streamline the authorization request flow. &quot;Confidential&quot; clients use JWTs signed with a secret key to authenticate the client software to Authorization Servers when making authorization requests.</p> <p>Automated client registration using client metadata is one of the more novel aspects of OAuth in atproto. As of August 2024, client metadata is still an Internet Draft (<a href="https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document/"><code>draft-parecki-oauth-client-id-metadata-document</code></a>); it should not be confused with the existing &quot;Dynamic Client Registration&quot; standard (<a href="https://datatracker.ietf.org/doc/html/rfc7591">RFC 7591</a>). We are hopeful other open protocols will adopt similar automated registration flows in the future, but there may not be general OAuth ecosystem support for some time.</p> <p>OAuth 2.0 is traditionally an authorization (<code>authz</code>) system, not an authentication (<code>authn</code>) system, meaning that it is not always a solution for pure account authentication use cases, such as &quot;Signup/Login with XYZ&quot; identity integrations. OpenID Connect (OIDC), which builds on top of OAuth 2.0, is usually the recommended standard for identity authentication. Unfortunately, the current version of OIDC does not enable authentication of atproto identities in a secure and generic way. The atproto profile of OAuth includes a (mandatory) mechanism for account authentication during the authorization flow and can be used for atproto identity authentication use cases.</p> <h2 class="scroll-mt-24" id="clients"><a class="group text-inherit no-underline hover:text-inherit" href="#clients">Clients</a></h2> <p>This section describes requirements for OAuth clients, which are enforced by Authorization Servers.</p> <p>OAuth client software is identified by a globally unique <code>client_id</code>. Distinct variants of client software may have distinct <code>client_id</code> values; for example the browser app and Android (mobile OS) variants of the same software might have different <code>client_id</code> values. As required by the <a href="https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document"><code>draft-parecki-oauth-client-id-metadata-document</code></a> specification draft, the <code>client_id</code> must be a fully-qualified web URL from which the client-metadata JSON document can be fetched. For example, <code>https://app.example.com/client-metadata.json</code>. Some more about the <code>client_id</code>:</p> <ul> <li>it must be a well-formed URL, following the W3C URL specification</li> <li>the schema must be <code>https://</code>, and there must not be a port number included. Note that there is a special exception for <code>http://localhost</code> <code>client_id</code> values for development, see details below</li> <li>the path does not need to include <code>client-metadata.json</code>, but it is helpful convention</li> </ul> <p>Authorization Servers which support both the atproto OAuth profile and other forms of OAuth should take care to prevent <code>client_id</code> value collisions. For example, <code>client_id</code> values for clients which are not auto-registered should never have the prefix <code>https://</code> or <code>http://</code>.</p> <h3>Types of Clients</h3> <p>All atproto OAuth clients need to meet a core set of standards and requirements, but there are a few variations in capabilities (such as session lifetime) depending on the security properties of the client itself.</p> <p>As described in the OAuth 2.0 specification (<a href="https://datatracker.ietf.org/doc/html/rfc6749">RFC 6749</a>), every client is one of two broad types:</p> <ul> <li><strong>confidential clients</strong> are clients which can authenticate themselves to Authorization Servers using a cryptographic signing key. This allows refresh tokens to be bound to the specific client. Note that this form of client authentication is distinct from DPoP: the client authentication key is common to all client sessions (although it can be rotated). This usually means that there is a web service controlled by the client which holds the key. Because they are authenticated and can revoke tokens in a security incident, confidential clients may be trusted with longer session and token lifetimes.</li> <li><strong>public clients</strong> do not authenticate using a client signing key, either because they don’t have a server-side component (the client software all runs on end-user devices), or they simply chose not to implement it.</li> </ul> <p>It is acceptable for a web service to act as a public client, and conversely it is possible for mobile apps and browser apps to coordinate with a token-mediating backend service and for the combination to form a confidential client. Mobile apps and browser apps can also adopt a &quot;backend-for-frontend&quot; (BFF) architecture with a web service backend acting as the OAuth client. This document will use the &quot;public&quot; vs &quot;confidential&quot; client terminology for clarity.</p> <p>The environment a client runs in also impacts the type of redirect (callback) URLs it uses during the Authorization Flow:</p> <ul> <li><strong>web clients</strong> include web services and browser apps. Redirect URLs are regular web URLs which open in a browser.</li> <li><strong>native clients</strong> include some mobile and desktop native clients. Redirect URLs may use platform-specific app callback schemes to open in the app itself.</li> </ul> <p>Authorization Servers may maintain a set of &quot;trusted&quot; clients, identified by <code>client_id</code>. Because any client could use unverified client metadata to impersonate a better-known app or brand, Authorization Servers should not display such metadata to end users in the Authorization Interface by default. Trusted clients can have additional metadata shown, such as a readable name (<code>client_name</code>), project URI (<code>client_uri</code>, which may have a different domain/origin than <code>client_id</code>) and logo (<code>logo_uri</code>). See the &quot;Security Considerations&quot; section for more details.</p> <p>Clients which are only using atproto OAuth for account authentication (without authorization to access PDS resources) should request minimal scopes (see &quot;Scopes&quot; section), but still need to implement most of the authorization flow. In particular, it is critical that they check the <code>sub</code> field in a token response to verify the account identity (this is an atproto-specific detail).</p> <h3>Client ID Metadata Document</h3> <div class="my-6 flex gap-2.5 rounded-2xl border border-blue-500/20 bg-blue-50/50 p-4 leading-6 text-blue-900 dark:border-blue-500/30 dark:bg-blue-500/5 dark:text-blue-200 dark:[--tw-prose-links-hover:theme(colors.blue.300)] dark:[--tw-prose-links:theme(colors.white)]"><svg viewBox="0 0 16 16" aria-hidden="true" class="mt-1 h-4 w-4 flex-none fill-blue-500 stroke-white dark:fill-blue-200/20 dark:stroke-blue-200"><circle cx="8" cy="8" r="8" stroke-width="0"></circle><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6.75 7.75h1.5v3.5"></path><circle cx="8" cy="4" r=".5" fill="none"></circle></svg><div class="[&amp;&gt;:first-child]:mt-0 [&amp;&gt;:last-child]:mb-0"><p>The Client ID Metadata Document specification (<a href="https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document/"><code>draft-parecki-oauth-client-id-metadata-document</code></a> is still a draft and may evolve over time. Our intention is to evolve and align with subsequent drafts and any final standard, while minimizing disruption and breakage with existing implementations.</p></div></div> <p>Clients must publish a &quot;client metadata&quot; JSON file on the public web. This will be fetched dynamically by Authorization Servers as part of the authorization request (PAR) and at other times during the session lifecycle. The response HTTP status must be 200 (not another 2xx or a redirect), with a JSON object body with the correct <code>Content-Type</code> (<code>application/json</code>).</p> <p>Authorization Servers need to fetch client metadata documents from the public web. They should use a hardened HTTP client for these requests (see &quot;OAuth Security Considerations&quot;). Servers may cache client metadata responses, optionally respecting HTTP caching headers (within limits). Minimum and maximum cache TTLs are not currently specified, but should be chosen to ensure that auth token requests predicated on stale confidential client authentication keys (<code>jwks</code> or <code>jwks_uris</code>) are rejected in a timely manner.</p> <p>The following fields are relevant for all client types:</p> <ul> <li><code>client_id</code> (string, required): the <code>client_id</code>. Must exactly match the full URL used to fetch the client metadata file itself</li> <li><code>application_type</code> (string, optional): must be one of <code>web</code> or <code>native</code>, with <code>web</code> as the default if not specified. Note that this is field specified by OpenID/OIDC, which we are borrowing. Used by the Authorization Server to enforce the relevant &quot;best current practices&quot;.</li> <li><code>grant_types</code> (array of strings, required): <code>authorization_code</code> must always be included. <code>refresh_token</code> is optional, but must be included if the client will make token refresh requests.</li> <li><code>scope</code> (string, sub-strings space-separated, required): all scope values which <em>might</em> be requested by this client are declared here. The <code>atproto</code> scope is required, so must be included here. See &quot;Scopes&quot; section.</li> <li><code>response_types</code> (array of strings, required): <code>code</code> must be included.</li> <li><code>redirect_uris</code> (array of strings, required): at least one redirect URI is required. See Authorization Request Fields section for rules about redirect URIs, which also apply here.</li> <li><code>token_endpoint_auth_method</code> (string, optional): confidential clients must set this to <code>private_key_jwt</code>.</li> <li><code>token_endpoint_auth_signing_alg</code> (string, optional): <code>none</code> is never allowed here. The current recommended and most-supported algorithm is <code>ES256</code>, but this may evolve over time. Authorization Servers will compare this against their supported algorithms.</li> <li><code>dpop_bound_access_tokens</code> (boolean, required): DPoP is mandatory for all clients, so this must be present and <code>true</code></li> <li><code>jwks</code> (object with array of JWKs, optional): confidential clients must supply at least one public key in JWK format for use with JWT client authentication. Either this field or the <code>jwks_uri</code> field must be provided for confidential clients, but not both.</li> <li><code>jwks_uri</code> (string, optional): URL pointing to a JWKS JSON object. See <code>jwks</code> above for details.</li> </ul> <p>These fields are optional but recommended:</p> <ul> <li><code>client_name</code> (string, optional): human-readable name of the client</li> <li><code>client_uri</code> (string, optional): not to be confused with <code>client_id</code>, this is a homepage URL for the client. If provided, the <code>client_uri</code> must have the same hostname as <code>client_id</code>.</li> <li><code>logo_uri</code> (string, optional): URL to client logo. Only <code>https:</code> URIs are allowed.</li> <li><code>tos_uri</code> (string, optional): URL to human-readable terms of service (ToS) for the client. Only <code>https:</code> URIs are allowed.</li> <li><code>policy_uri</code> (string, optional): URL to human-readable privacy policy for the client. Only <code>https:</code> URIs are allowed.</li> </ul> <p>See &quot;OAuth Security Considerations&quot; below for when <code>client_name</code>, <code>client_uri</code>, and <code>logo_uri</code> will or will not be displayed to end users.</p> <p>Additional optional client metadata fields are enumerated with <a href="https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata">IANA</a>. Note that these are shared with the &quot;Dynamic Client Registration&quot; standard, which is not used directly by the atproto OAuth profile.</p> <h3>Localhost Client Development</h3> <p>When working with a developent environment (Authorization Server and Client), it may be difficult for developers to publish in-progress client metadata at a public URL so that authorization servers can access it. This may even be true for development environments using a containerized Authorization Server and local DNS, because of SSRF protections against local IP ranges.</p> <p>To make development workflows easier, a special exception is made for clients with <code>client_id</code> having origin <code>http://localhost</code> (with no port number specified). Authorization Servers are encouraged to support this exception - including in production environments - but it is optional.</p> <p>In a localhost <code>client_id</code> scenario, the Authorization Server should verify that the scheme is <code>http</code>, and that the hostname is exactly <code>localhost</code> with no port specified. IP addresses (<code>127.0.0.1</code>, etc) are not supported. The path parameter must be empty (<code>/</code>).</p> <p>In the Authorization Request, the <code>redirect_uri</code> must match one of those supplied (or a default). Path components must match, but port numbers are not matched.</p> <p>Some metadata fields can be configured via query parameter in the <code>client_id</code> URL (with appropriate urlencoding):</p> <ul> <li><code>redirect_uri</code> (string, multiple query parameters allowed, optional): allows declaring a local redirect/callback URL, with path component matched but port numbers ignored. The default values (if none are supplied) are <code>http://127.0.0.1/</code> and <code>http://[::1]/</code>.</li> <li><code>scope</code> (string with space-separated values, single query parameter allowed, optional): the set of scopes which might be requested by the client. Default is <code>atproto</code>.</li> </ul> <p>The other parameters in the virtual client metadata document will be:</p> <ul> <li><code>client_id</code> (string): the exact <code>client_id</code> (URL) used to generate the virtual document</li> <li><code>client_name</code> (string): a value chosen by the Authorization Server (e.g. &quot;Development client&quot;)</li> <li><code>response_types</code> (array of strings): must include <code>code</code></li> <li><code>grant_types</code> (array of strings): <code>authorization_code</code> and <code>refresh_token</code></li> <li><code>token_endpoint_auth_method</code>: <code>none</code></li> <li><code>application_type</code>: <code>native</code></li> <li><code>dpop_bound_access_tokens</code>: <code>true</code></li> </ul> <p>Note that this works as a public client, not a confidential client.</p> <h2 class="scroll-mt-24" id="identity-authentication"><a class="group text-inherit no-underline hover:text-inherit" href="#identity-authentication">Identity Authentication</a></h2> <p>As mentioned in the introduction, OAuth 2.0 generally provides only Authorization (<code>authz</code>), and additional standards like OpenID/OIDC are used for Authentication (<code>authn</code>). The atproto profile of OAuth requires authentication of account identity and supports the use case of simple identity authentication without additional resource access authorization.</p> <p>In atproto, account identity is anchored in the account DID, which is the permanent, globally unique, publicly resolvable identifier for the account. The DID resolves to a DID document which indicates the current PDS host location for the account. That PDS (combined with an optional entryway) is the authorization authority and the OAuth Authorization Server for the account. When speaking to any Authorization Server, it is critical (mandatory) for clients to confirm that it is actually the authoritative server for the account in question, which means independently resolving the account identity (by DID) and confirming that the Authorization Server matches. It is also critical (mandatory) to confirm at the end of an authorization flow that the Authorization Server actually authorized the expected account. The reason this is necessary is to confirm that the Authorization Server is authoritative for the account in question. Otherwise a malicious server could authenticate arbitrary accounts (DIDs) to the client.</p> <p>Clients can start an auth flow in one of two ways:</p> <ul> <li>starting with a public account identifier, provided by the user: handle or DID</li> <li>starting with a server hostname, provided by the user: PDS or entryway, mapping to either Resource Server and/or Authorization Server</li> </ul> <p>One use case for starting with a server instead of an account identifier is when the user does not remember their full account handle or only knows their account email. Another is for authentication when a user’s handle is broken. The user will still need to know their hosting provider in these situation.</p> <p>When starting with an account identifier, the client must resolve the atproto identity to a DID document. If starting with a handle, it is critical (mandatory) to bidirectionally verify the handle by checking that the DID document claims the handle (see atproto Handle specification). All handle resolution techniques and all atproto-blessed DID methods must be supported to ensure interoperability with all accounts.</p> <p>In some client environments, it may be difficult to resolve all identity types. For example, handle resolution may involve DNS TXT queries, which are not directly supported from browser apps. Client implementations might use alternative techniques (such as DNS-over-HTTP) or could make use of a supporting web service to resolve identities.</p> <p>Because authorization flows are security-critical, any caching of identity resolution should choose cache lifetimes carefully. Cache lifetimes of less than 10 minutes are recommended for auth flows specifically.</p> <p>The resolved DID should be bound to the overall auth session and should be used as the primary account identifier within client app code. Handles (when verified) are acceptable to display in user interfaces, but may change over time and need to be re-verified periodically. When passing an account identifier through to the Authorization Server as part of the Authorization Request in the <code>login_hint</code>, it is recommended to use the exact account identifier supplied by the user (handle or DID) to ensure any sign-in flow is consistent (users might not recognize their own account DID).</p> <p>At the end of the auth flow, when the client does an initial token fetch, the Authorization Server must return the account DID in the <code>sub</code> field of the JSON response body. If the entire auth flow started with an account identifier, it is critical for the client to verify that this DID matches the expected DID bound to the session earlier; the linkage from account to Authorization Server will already have been verified in this situation.</p> <p>If the auth flow instead starts with a server (hostname or URL), the client will first attempt to fetch Resource Server metadata (and resolve to Authorization Server if found) and then attempt to fetch Authorization Server metadata. See &quot;Authorization Server&quot; section for server metadata fetching. If either is successful, the client will end up with an identified Authorization Server. The Authorization Request flow will proceed without a <code>login_hint</code> or account identifier being bound to the session, but the Authorization Server <code>issuer</code> will be bound to the session.</p> <p>After the auth flow continues and an initial token request succeeds, the client will parse the account identifier from the <code>sub</code> field in the token response. At this point, the client still cannot trust that it has actually authenticated the indicated account. It is critical for the client to resolve the identity (DID document), extract the declared PDS host, confirm that the PDS (Resource Server) resolves to the Authorization Server bound to the session by fetching the Resource Server metadata, and fetch the Authorization Server metadata to confirm that the <code>issuer</code> field matches the Authorization Server origin (see <a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-7.13.1"><code>draft-ietf-oauth-v2-1</code> section 7.3.1</a> regarding this last point).</p> <p>To reiterate, it is critical for all clients - including those only interested in atproto Identity Authentication - to go through the entire Authorization flow and to verify that the account identifier (DID) in the <code>sub</code> field of the token response is consistent with the Authorization Server hostname/origin (<code>issuer</code>).</p> <h2 class="scroll-mt-24" id="authorization-scopes"><a class="group text-inherit no-underline hover:text-inherit" href="#authorization-scopes">Authorization Scopes</a></h2> <p>OAuth scopes allow more granular control over the resources and actions a client is granted access to.</p> <p>The special <code>atproto</code> scope is required for all atproto OAuth sessions. The semantics are somewhat similar to the <code>openid</code> scope: inclusion of it confirms that the client is using the atproto profile of OAuth and will comply with all the requirements laid out in this specification. No access to any atproto-specific PDS resources will be granted without this scope included.</p> <p>Authorization Servers may support other profiles of OAuth if client does not include the <code>atproto</code> scope. For example, an Authorization Server might function as both an atproto PDS/entryway, and support other protocols/standards at the same time.</p> <p>Use of the atproto OAuth profile, as indicated by the <code>atproto</code> scope, means that the Authorization Server will return the atproto account DID as an account identifier in the <code>sub</code> field of token requests. Authorization Servers must return <code>atproto</code> in <code>scopes_supported</code> in their metadata document, so that clients know they support the atproto OAuth profile. A client may include only the <code>atproto</code> scope if they only need account authentication - for example a &quot;Login with atproto&quot; use case. Unlike OpenID, profile metadata in atproto is generally public, so an additional authorization scope for fetching profile metadata is not needed.</p> <p>The OAuth 2.0 specification does not require Authorization Servers to return the granted scopes in the token responses unless the scope that was granted is different from what the client requested. In the atproto OAuth profile, servers must always return the granted scopes in the token response. Clients should reject token responses if they don&#x27;t contain a <code>scope</code> field, or if the <code>scope</code> field does not contain <code>atproto</code>.</p> <p>The intention is to support flexible scopes based on Lexicon namespaces (NSIDs) so that clients can be given access only to the specific content and API endpoints they need access to. Until the design of that scope system is ready, the atproto profile of OAuth defines two transitional scopes which align with the permissions granted under the original &quot;session token&quot; auth system:</p> <ul> <li><code>transition:generic</code>: broad PDS account permissions, equivalent to the previous &quot;App Password&quot; authorization level.<!-- --> <ul> <li>write (create/update/delete) any repository record type</li> <li>upload blobs (media files)</li> <li>read and write any personal preferences</li> <li>API endpoints and service proxying for most Lexicon endpoints, to any service provider (identified by DID)</li> <li>ability to generate service auth tokens for the specific API endpoints the client has access to</li> <li>no account management actions: change handle, change email, delete or deactivate account, migrate account</li> <li>no access to DMs (the <code>chat.bsky.*</code> Lexicons), specifically</li> </ul> </li> <li><code>transition:chat.bsky</code>: equivalent to adding the &quot;DM Access&quot; toggle for &quot;App Passwords&quot;<!-- --> <ul> <li>API endpoints and service proxying for the <code>chat.bsky</code> Lexicons specifically</li> <li>ability to generate service auth tokens for the <code>chat.bsky</code> Lexicons</li> <li>this scope depends on and does not function without the <code>transition:generic</code> scope</li> </ul> </li> </ul> <h2 class="scroll-mt-24" id="authorization-requests"><a class="group text-inherit no-underline hover:text-inherit" href="#authorization-requests">Authorization Requests</a></h2> <p>This section details standards and requirements specific to Authorization Requests.</p> <p>PKCE and PAR are required for all client types and Authorization Servers. Confidential clients authenticate themselves using JWT client assertions.</p> <h3>Request Fields</h3> <p>A summary of fields relevant to authorization requests with the atproto OAuth profile:</p> <ul> <li><code>client_id</code> (string, required): identifies the client software. See &quot;Clients&quot; section above for details.</li> <li><code>response_type</code> (string, required): must be <code>code</code></li> <li><code>code_challenge</code> (string, required): the PKCE challenge value. See &quot;PKCE&quot; section.</li> <li><code>code_challenge_method</code> (string, required): which code challenge method is used, for example <code>S256</code>. See &quot;PKCE&quot; section.</li> <li><code>state</code> (string, required): random token used to verify the authorization request against the response. See below.</li> <li><code>redirect_uri</code> (string, required): must match against URIs declared in client metadata and have a format consistent with the <code>application_type</code> declared in the client metadata. See below.</li> <li><code>scope</code> (string with space-separated values, required): must be a subset of the scopes declared in client metadata. Must include <code>atproto</code>. See &quot;Scopes&quot; section.</li> <li><code>client_assertion_type</code> (string, optional): used by confidential clients to describe the client authentication mechanism. See &quot;Confidential Client&quot; section.</li> <li><code>client_assertion</code> (string, optional): only used for confidential clients, for client authentication. See &quot;Confidential Client&quot; section.</li> <li><code>login_hint</code> (string, optional): account identifier to be used for login. See &quot;Authorization Interface&quot; section.</li> </ul> <p>The <code>client_secret</code> value, used in many other OAuth profiles, should not be included.</p> <p>The <code>state</code> parameter in client authorization requests is mandatory. Clients should use randomly-generated tokens for this parameter and not have collisions or reuse tokens across any combination of device, account, or session. Authorization Servers should reject duplicate state parameters, but are not currently required to track state values across accounts or sessions. The <code>state</code> parameter is effectively used to verify the <code>issuer</code> later, and it is important that the parameter can not be forged or guessed by an untrusted party.</p> <p>For web clients, the <code>redirect_uri</code> is a HTTPS URL which will be redirected in the browser to return users to the application at the end of the Authorization flow. The URL may include a port number, but not if it is the default port number. The <code>redirect_uri</code> must match one of the URIs declared in the client metadata and the Authorization Server must verify this condition. The URL origin must match that of the <code>client_id</code>.</p> <p>There is a special exception for the localhost development workflow to use <code>http://127.0.0.1</code> or <code>http://[::1]</code> URLs, with matching rules described in the &quot;Localhost Client Development&quot; section. These clients use web URLs, but have <code>application_type</code> set to <code>native</code> in the generated client metadata.</p> <p>For native clients, the <code>redirect_uri</code> may use a custom URI scheme to have the operating system redirect the user back to the app, instead of a web browser. Native clients are also allowed to use an HTTPS URL. Any custom scheme must match the <code>client_id</code> hostname in reverse-domain order. The URI scheme must be followed by a single colon (<code>:</code>) then a single forward slash (<code>/</code>) and then a URI path component. For example, an app with <code>client_id</code> <a href="https://app.example.com/client-metadata.json"><code>https://app.example.com/client-metadata.json</code></a> could have a <code>redirect_uri</code> of <code>com.example.app:/callback</code>.</p> <p>Native clients are also allowed to use an HTTPS URL. In this case, the URL origin must be the same as the <code>client_id</code>. One example use-case is &quot;Apple Universal Links&quot;.</p> <p>Clients may include additional optional authorization request parameters - and servers may process them - but they are not required to. Refer to other OAuth standards and the <a href="https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml">IANA OAuth parameter registry</a>.</p> <h3>Proof Key for Code Exchange (PKCE)</h3> <p>PKCE is mandatory for all Authorization Requests. Clients must generate new, unique, random challenges for every authorization request. Authorization Servers must prevent reuse of <code>code_challenge</code> values across sessions (at least within some reasonable time frame, such as a 24 hour period).</p> <p>The <code>S256</code> challenge method must be supported by all clients and Authorization Servers; see <a href="https://datatracker.ietf.org/doc/html/rfc7636">RFC 7636</a> for details. The <code>plain</code> method is not allowed. Additional methods may in theory be supported if both client and server support them.</p> <p>Authorization Servers should reject reuse of a <code>code</code> value, and revoke any outstanding sessions and tokens associated with the earlier use of the <code>code</code> value.</p> <h3>Pushed Authorization Requests (PAR)</h3> <p>Authorization Servers must support PAR and clients of all types must use PAR for Authorization Requests.</p> <p>Authorization Servers must set <code>require_pushed_authorization_requests</code> to <code>true</code> in their server metadata document and include a valid URL in <code>pushed_authorization_request_endpoint</code>. See <a href="https://datatracker.ietf.org/doc/html/rfc9207">RFC 9207</a> for requirements on this URL.</p> <p>Clients make an HTTPS POST request to the <code>pushed_authorization_request_endpoint</code> URL, with the request parameters in the form-encoded request body. They receive a <code>request_uri</code> (not to be confused with <code>redirect_uri</code>) in the JSON response object. When they redirect the user to the authorization endpoint (<code>authorization_endpoint</code>), they omit most of the request parameters they already sent and include this <code>redirect_uri</code> along with <code>client_id</code> as query parameters instead.</p> <div class="my-6 flex gap-2.5 rounded-2xl border border-blue-500/20 bg-blue-50/50 p-4 leading-6 text-blue-900 dark:border-blue-500/30 dark:bg-blue-500/5 dark:text-blue-200 dark:[--tw-prose-links-hover:theme(colors.blue.300)] dark:[--tw-prose-links:theme(colors.white)]"><svg viewBox="0 0 16 16" aria-hidden="true" class="mt-1 h-4 w-4 flex-none fill-blue-500 stroke-white dark:fill-blue-200/20 dark:stroke-blue-200"><circle cx="8" cy="8" r="8" stroke-width="0"></circle><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6.75 7.75h1.5v3.5"></path><circle cx="8" cy="4" r=".5" fill="none"></circle></svg><div class="[&amp;&gt;:first-child]:mt-0 [&amp;&gt;:last-child]:mb-0"><p>PAR is a relatively new and less-supported standard, and the requirement to use PAR may be relaxed if it is found to be too onerous a requirement for client implementations. In that case, Authorization Servers would be required to support both PAR and non-PAR requests with PAR being optional for clients.</p></div></div> <h3>Confidential Client Authentication</h3> <p>Confidential clients authenticate themselves during the Authorization Request using a JWT client assertion. Authorization Servers may grant confidential clients longer token/session lifetimes. See &quot;Tokens&quot; section for more context.</p> <p>The client assertion type to use is <code>urn:ietf:params:oauth:client-assertion-type:jwt-bearer</code>, as described in &quot;JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants&quot; (<a href="https://datatracker.ietf.org/doc/html/rfc7523">RFC 7523</a>). Clients and Authorization Servers currently must support the <code>ES256</code> cryptographic system. The set of recommended systems/algorithms is expected to evolve over time.</p> <p>Additional requirements:</p> <ul> <li>confidential clients must publish one or more client authentication keys (public key) in the client metadata. This can be either direct JWK format as JSON in the <code>jwks</code> field, or as a separate JWKS JSON object on the web linked by a <code>jwks_uri</code> URL. A <code>jwks_uri</code> URL must be a valid fully qualified URL with <code>https://</code> scheme.</li> <li>confidential clients should periodically rotate client keys, adding new keys to the JWKS set and using then for new sessions, then removing old keys once they are no longer associated with any active auth sessions</li> <li>confidential clients must include <code>token_endpoint_auth_method</code> as <code>private_key_jwt</code> in their client metadata document</li> <li>confidential clients are expected to immediately remove client authentication keys from their client metadata if the key has been leaked or compromised</li> <li>Authorization Servers must bind active auth sessions for confidential clients to the client authentication key used at the start of the session. The server should revoke the session and reject further token refreshes if the client authentication key becomes absent from the client metadata. This means the Authorization Server is expected to periodically re-fetch client metadata.</li> </ul> <h2 class="scroll-mt-24" id="tokens-and-session-lifetime"><a class="group text-inherit no-underline hover:text-inherit" href="#tokens-and-session-lifetime">Tokens and Session Lifetime</a></h2> <p>Access tokens are used to authorize client requests to the account&#x27;s PDS (&quot;Resource Server&quot;). From the standpoint of the client they are opaque, but they are often signed JWTs including an expiration time. Depending on the PDS implementation, it may or may not be possible to revoke individual access tokens in the event of a compromise, so they must be restricted to a relatively short lifetime.</p> <p>Refresh tokens are used to request new tokens (of both types) from the Authorization Server (PDS or entryway). They are also opaque from the standpoint of clients. Auth sessions can be revoked - invalidating the refresh tokens - so they may have a longer lifetime. In the atproto OAuth profile, refresh tokens are generally single-use, with the &quot;new&quot; refresh token replacing that used in the token request. This means client implementations may need locking primitives to prevent concurrent token refresh requests.</p> <p>To request refresh tokens, the client must declare <code>refresh_token</code> as a grant type in their client metadata.</p> <p>Tokens are always bound to a unique session DPoP key. Tokens must not be shared or reused across client devices. They must also be uniquely bound to the client software (<code>client_id</code>). The overall session ends when the access and refresh tokens can no longer be used.</p> <p>The specific lifetime of sessions, access tokens, and refresh tokens is up to the Authorization Server implementation and may depend on security assessments of client type and reputation.</p> <p>Some guidelines and requirements:</p> <ul> <li>access token lifetimes should be less than 30 minutes in all situations. If the server cannot revoke individual access tokens then the maximum is 15 minutes, and 5 minutes is recommended.</li> <li>for &quot;untrusted&quot; public clients, overall session lifetime should be limited to 7 days, and the lifetime of individual refresh tokens should be limited to 24 hours</li> <li>for confidential clients, the overall session lifetime may be unlimited. Individual refresh tokens should have a lifetime limited to 180 days</li> <li>confidential clients must use the same client authentication key and assertion method for refresh token requests that they did for the initial authentication request</li> </ul> <h2 class="scroll-mt-24" id="demonstrating-proof-of-possession-d-po-p"><a class="group text-inherit no-underline hover:text-inherit" href="#demonstrating-proof-of-possession-d-po-p">Demonstrating Proof of Possession (DPoP)</a></h2> <p>The atproto OAuth profile mandates use of DPoP for all client types when making auth token requests to the Authorization Server and when making authorized requests to the Resource Server. See <a href="https://datatracker.ietf.org/doc/html/rfc9449">RFC 9449</a> for details.</p> <p>Clients must initiate DPoP in the initial authorization request (PAR).</p> <p>Server-provided DPoP nonces are mandatory. The Resource Server and Authorization Server may share nonces (especially if they are the same server) or they may have separate nonces. Clients should track the DPoP nonce per account session and per server. Servers must rotate nonces periodically, with a maximum lifetime of 5 minutes. Servers may use the same nonce across all client sessions and across multiple requests at any point in time. Servers should accept recently-stale (old) nonces to make rotation smoother for clients with multiple concurrent request in-flight. Clients should be resilient to unexpected nonce updates in the form of HTTP 400 errors and should retry those failed requests. Clients must reject responses missing a <code>DPoP-Nonce</code> header (case insensitive), if the request included DPoP.</p> <p>Clients must generate and sign a unique DPoP token (JWT) for every request. Each DPoP request JWT must have a unique (randomly generated) <code>jti</code> nonce. Servers should prevent token replays by tracking <code>jti</code> nonces and rejecting re-use. They can restrict their client-generated <code>jti</code> nonce history to the server-generated DPoP nonce so that they do not need to track an endlessly growing set of nonces.</p> <p>The <code>ES256</code> (NIST &quot;P-256&quot;) cryptographic algorithm must be supported by all clients and servers for DPoP JWT signing. The set of algorithms recommended for use is expected to evolve over time. Clients and Servers may implement additional algorithms and declare them in metadata documents to facilitate cryptographic evolution and negotiation.</p> <h2 class="scroll-mt-24" id="authorization-servers"><a class="group text-inherit no-underline hover:text-inherit" href="#authorization-servers">Authorization Servers</a></h2> <p>To enable browser apps, Authorization Servers must support HTTP CORS requests/headers on relevant endpoints, including server metadata, auth requests (PAR), and token requests.</p> <h3>Server Metadata</h3> <p>Both Resource Servers (PDS instances) and Authorization Servers (PDS or entryway) need to publish metadata files at well-known HTTPS endpoints.</p> <p>Resource Server (PDS) metadata must comply with the &quot;OAuth 2.0 Protected Resource Metadata&quot; (<a href="https://datatracker.ietf.org/doc/draft-ietf-oauth-resource-metadata/"><code>draft-ietf-oauth-resource-metadata</code></a>) draft specification. A summary of requirements:</p> <ul> <li>the URL path is <code>/.well-known/oauth-protected-resource</code></li> <li>response must be an HTTP 200 (not 2xx or redirect), and must be a valid JSON object with content type <code>application/json</code></li> <li>must contain an <code>authorization_servers</code> array of strings, with a single element, which is a fully-qualified URL</li> </ul> <p>The Authorization Server URL may be the same origin as the Resource Server (PDS), or might point to a separate server (e.g. entryway). The URL must be a simple origin URL: <code>https</code> scheme, no credentials (user:password), no path, no query string or fragment. A port number is allowed, but a default port (443 for HTTPS) must not be included.</p> <p>The Authorization Server also publishes metadata, complying with the &quot;OAuth 2.0 Authorization Server Metadata&quot; (<a href="https://datatracker.ietf.org/doc/html/rfc8414">RFC 8414</a>) standard. A summary of requirements:</p> <ul> <li>the URL path is <code>/.well-known/oauth-authorization-server</code></li> <li>response must be an HTTP 200 (not 2xx or redirect), and must be a valid JSON object with content type <code>application/json</code></li> <li><code>issuer</code> (string, required): the &quot;origin&quot; URL of the Authorization Server. Must be a valid URL, with <code>https</code> scheme. A port number is allowed (if that matches the origin), but the default port (443 for HTTPS) must not be specified. There must be no path segments. Must match the origin of the URL used to fetch the metadata document itself.</li> <li><code>authorization_endpoint</code> (string, required): endpoint URL for authorization redirects</li> <li><code>token_endpoint</code> (string, required): endpoint URL for token requests</li> <li><code>response_types_supported</code> (array of strings, required): must include <code>code</code></li> <li><code>grant_types_supported</code> (array of strings, required): must include <code>authorization_code</code> and <code>refresh_token</code> (refresh tokens must be supported)</li> <li><code>code_challenge_methods_supported</code> (array of strings, required): must include <code>S256</code> (see &quot;PKCE&quot; section)</li> <li><code>token_endpoint_auth_methods_supported</code> (array of strings, required): must include both <code>none</code> (public clients) and <code>private_key_jwt</code> (confidential clients)</li> <li><code>token_endpoint_auth_signing_alg_values_supported</code> (array of strings, required): must not include <code>none</code>. Must include <code>ES256</code> for now. Cryptographic algorithm suites are expected to evolve over time.</li> <li><code>scopes_supported</code> (space-separated string, required): must include <code>atproto</code>. If supporting the transitional grants, they should be included here as well. See &quot;Scopes&quot; section.</li> <li><code>authorization_response_iss_parameter_supported</code> (boolean): must be <code>true</code></li> <li><code>require_pushed_authorization_requests</code> (boolean): must be <code>true</code>. See &quot;PAR&quot; section.</li> <li><code>pushed_authorization_request_endpoint</code> (string, required): must be the PAR endpoint URL. See &quot;PAR&quot; section.</li> <li><code>dpop_signing_alg_values_supported</code> (array of strings, required): currently must include <code>ES256</code>. See &quot;DPoP&quot; section.</li> <li><code>require_request_uri_registration</code> (boolean, optional): default is <code>true</code>; does not need to be set explicitly, but must not be <code>false</code></li> <li><code>client_id_metadata_document_supported</code> (boolean, required): must be <code>true</code>. See &quot;Client ID Metadata&quot; section.</li> </ul> <p>The <code>issuer</code> (&quot;origin&quot;) is the overall identifier for the Authorization Server.</p> <h3>Authorization Interface</h3> <p>The Authorization Server (PDS/entryway) must implement a web interface for users to authenticate with the server, approve (or reject) Authorization Requests from clients, and manage active sessions. This is called the &quot;Authorization Interface&quot;.</p> <p>Server implementations can chose their own technology for user authentication and account recovery: secure cookies, email, various two-factor authentication, passkeys, external identity providers (including upstream OpenID/OIDC), etc. Servers may also support multiple concurrent auth sessions with users.</p> <p>When a client redirects to the Authorization Server’s authorization URL (the declared <code>authorization_endpoint</code>), the server first needs to authenticate the user. If there is no active auth session, the user may be prompted to log in. If a <code>login_hint</code> was provided in the Authorization Request, that can be used to pre-populate the login form. If there are multiple active auth sessions, the user could be prompted to select one from a list, or the <code>login_hint</code> could be used to auto-select. If there is a single active session, the interface can move to the approval view, possibly with the option to login as a different account. If a <code>login_hint</code> was supplied, the Authorization Server should only allow the user to authenticate with that account. Otherwise the overall authorization flow will fail when the client verifies the account identity (<code>sub</code> field).</p> <p>The authorization approval prompt should identify the client app and describe the scope of authorization that has been requested.</p> <p>The amount of client metadata that should be displayed may depend on whether the client is &quot;trusted&quot; by the Authorization Server; see the &quot;Client&quot; and &quot;Security Concerns&quot; sections. The full <code>client_id</code> URL should be displayed by default.</p> <p>See the &quot;Scopes&quot; section for a description of scope options.</p> <p>If a client is a confidential client and the user has already approved the same scopes for the same client in the past, the Authorization Server may allow &quot;silent sign-in&quot; by auto-approving the request. Authorization Servers can set their own policies for this flow: it may require explicit user configuration, or the client may be required to be &quot;trusted&quot;.</p> <p>Authorization Servers should separately implement a web interface which allows authenticated users to view active OAuth sessions and delete them.</p> <h2 class="scroll-mt-24" id="summary-of-authorization-flow"><a class="group text-inherit no-underline hover:text-inherit" href="#summary-of-authorization-flow">Summary of Authorization Flow</a></h2> <p>This is a high-level description of what an atproto OAuth authorization flow looks like. It assumes the user already has an atproto account.</p> <p>The client starts by asking for the user’s account identifier (handle or DID), or for a PDS/entryway hostname. See &quot;Identity Authentication&quot; section for details.</p> <p>For an account identifier, the client resolves the identity to a DID document, extracts the declared PDS URL, then fetches the Resource Server and Authorization Server locations. If starting with a server hostname, the client resolves that hostname to an Authorization Server. In either case, Authorization Server metadata is fetched and verified against requirements for atproto OAuth (see &quot;Authorization Server&quot; section).</p> <p>The client next makes a Pushed Authorization Request via HTTP POST request. See &quot;Authorization Request&quot; section; some notable details include:</p> <ul> <li>a randomly generated <code>state</code> token is required, and will be used to reference this authorization request with the subsequent response callback</li> <li>PKCE is required, so a secret value is generated and stored, and a derived challenge is included in the request</li> <li><code>scope</code> values are requested here, and must include <code>atproto</code></li> <li>for confidential clients, a <code>client_assertion</code> is included, with type <code>jwt-bearer</code>, signed using the secret client authentication key</li> <li>the client generates a new DPoP key for the user/device/session and uses it starting with the PAR request</li> <li>if the auth flow started with an account identifier, the client should pass that starting identifier via the <code>login_hint</code> field</li> <li>atproto uses PAR, so the request will be sent as an HTTP POST request to the Authorization Server</li> </ul> <p>The Authorization Server will receive the PAR request and use the <code>client_id</code> URL to resolve the client metadata document. The server validates the request and client metadata, then stores information about the session, including binding a DPoP key to the session. The server returns a <code>request_uri</code> token to the client, including a DPoP nonce via HTTP header.</p> <p>The client receives the <code>request_uri</code> and prepares to redirect the user. At this point, the client usually needs to persist information about the session to some type of secure storage, so it can be read back after the redirect returns. This might be a database (for a web service backend) or web platform storage like IndexedDB (for a browser app). The client then redirects the user via browser to the Authorization Server’s auth endpoint, including the <code>request_uri</code> as a URL parameter. In any case, clients must not store session data directly in the <code>state</code> request param.</p> <p>The Authorization Server uses the <code>request_uri</code> to look up the earlier Authorization Request parameters, authenticates the user (which might include sign-in or account selection), and prompts the user with the Authorization Interface. The user might refine any granular requested scopes, then approves or rejects the request. The Authorization Server redirects the user back to the <code>redirect_uri</code>, which might be a web callback URL, or a native app URI (for native clients).</p> <p>The client uses URL query parameters (<code>state</code> and <code>iss</code>) to look up and verify session information. Using the <code>code</code> query parameter, the client then makes an initial token request to the Authorization Server’s token endpoint. The client completes the PKCE flow by including the earlier value in the <code>code_verifier</code> field. Confidential clients need to include a client assertion JWT in the token request; see the &quot;Confidential Client&quot; section. The Authorization Server validates the request and returns a set of tokens, as well as a <code>sub</code> field indicating the account identifier (DID) for this session, and the <code>scope</code> that is covered by the issued access token.</p> <p>At this point it is critical (mandatory) for all clients to verify that the account identified by the <code>sub</code> field is consistent with the Authorization Server &quot;issuer&quot; (present in the <code>iss</code> query string), either by validating against the originally-supplied account DID, or by resolving the accounts DID to confirm the PDS is consistent with the Authorization Server. See &quot;Identity Authentication&quot; section. The Authorization always returns the scopes approved for the session in the <code>scopes</code> field (even if they are the same as the request, as an atproto OAuth profile requirement), which may reflect partial authorization by the user. Clients must reject the session if the response does not include <code>atproto</code> in the returned scopes.</p> <p>Authentication-only clients can end the flow here.</p> <p>Using the access token, clients are now able to make authorized requests to the PDS (&quot;Resource Server&quot;). They must use DPoP for all such requests, along with the access token. Tokens (both access and refresh) will need to be periodically &quot;refreshed&quot; by subsequent request to the Authorization Server token endpoint. These also require DPoP. See &quot;Tokens and Session Lifetime&quot; section for details.</p> <h2 class="scroll-mt-24" id="security-considerations"><a class="group text-inherit no-underline hover:text-inherit" href="#security-considerations">Security Considerations</a></h2> <p>There are a number of situations where HTTP URLs provided by external parties are fetched by both clients and providers (servers). Care must be taken to prevent harmful fetches due to maliciously crafted URLs, including hostnames which resolve to private or internal IP addresses. The general term for this class of security issue is Server-Side Request Forgery (SSRF). There is also a class of denial-of-service attacks involving HTTP requests to malicious servers, such as huge response bodies, TCP-level slow-loris attacks, etc. We strongly recommend using &quot;hardened&quot; HTTP client implementations/configurations to mitigate these attacks.</p> <p>Any party can create a client and client metadata file with any contents at any time. Even the hostname in the <code>client_id</code> cannot be entirely trusted to represent the overall client: an untrusted user may have been able to upload the client metadata file to an arbitrary URL on the host. In particular, the <code>client_uri</code>, <code>client_name</code>, and <code>logo_uri</code> fields are not verified and could be used by a malicious actor to impersonate a legitimate client. It is strongly recommended for Authorization Servers to not display these fields to end users during the auth flow for unknown clients. Service operators may maintain a list of &quot;trusted&quot; <code>client_id</code> values and display the extra metadata for those apps only.</p> <h2 class="scroll-mt-24" id="possible-future-changes"><a class="group text-inherit no-underline hover:text-inherit" href="#possible-future-changes">Possible Future Changes</a></h2> <p>Client metadata requests (by the authorization server) might fail for any number of reasons: transient network disruptions, the client server being down for regular maintenance, etc. It seems brittle for the Authorization Server to immediately revoke access to active client sessions in this scenario. Maybe there should be an explicit grace period?</p> <p>The requirement that resource server metadata only have a single URL reference to an authorization server might be relaxed.</p> <p>The details around session and token lifetimes might change with further security review.</p></div></article></main><footer class="mx-auto w-full max-w-2xl space-y-10 pb-16 lg:max-w-5xl"><div class="flex"><div class="flex flex-col items-start gap-3"><a class="inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300" aria-label="Previous: HTTP API (XRPC)" href="/specs/xrpc"><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="mt-0.5 h-5 w-5 -ml-1 rotate-180"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"></path></svg>Previous</a><a tabindex="-1" aria-hidden="true" class="text-base font-semibold text-zinc-900 transition hover:text-zinc-600 dark:text-white dark:hover:text-zinc-300" href="/specs/xrpc">HTTP API (XRPC)</a></div><div class="ml-auto flex flex-col items-end gap-3"><a class="inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300" aria-label="Next: Event Stream" href="/specs/event-stream">Next<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="mt-0.5 h-5 w-5 -mr-1"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"></path></svg></a><a tabindex="-1" aria-hidden="true" class="text-base font-semibold text-zinc-900 transition hover:text-zinc-600 dark:text-white dark:hover:text-zinc-300" href="/specs/event-stream">Event Stream</a></div></div><div class="flex flex-col items-center justify-between gap-5 border-t border-zinc-900/5 pt-8 sm:flex-row dark:border-white/5"><p class="text-xs text-zinc-600 dark:text-zinc-400">© Copyright <!-- -->2024<!-- -->. All rights reserved.</p><div class="flex gap-4"><a class="group" href="https://bsky.app/profile/atproto.com"><span class="sr-only">Follow us on Bluesky</span><svg viewBox="0 0 360 320" aria-hidden="true" class="h-5 w-5 fill-zinc-700 transition group-hover:fill-zinc-900 dark:group-hover:fill-zinc-500"><path d="M180 141.964C163.699 110.262 119.308 51.1817 78.0347 22.044C38.4971 -5.86834 23.414 -1.03207 13.526 3.43594C2.08093 8.60755 0 26.1785 0 36.5164C0 46.8542 5.66748 121.272 9.36416 133.694C21.5786 174.738 65.0603 188.607 105.104 184.156C107.151 183.852 109.227 183.572 111.329 183.312C109.267 183.642 107.19 183.924 105.104 184.156C46.4204 192.847 -5.69621 214.233 62.6582 290.33C137.848 368.18 165.705 273.637 180 225.702C194.295 273.637 210.76 364.771 295.995 290.33C360 225.702 313.58 192.85 254.896 184.158C252.81 183.926 250.733 183.645 248.671 183.315C250.773 183.574 252.849 183.855 254.896 184.158C294.94 188.61 338.421 174.74 350.636 133.697C354.333 121.275 360 46.8568 360 36.519C360 26.1811 357.919 8.61012 346.474 3.43851C336.586 -1.02949 321.503 -5.86576 281.965 22.0466C240.692 51.1843 196.301 110.262 180 141.964Z"></path></svg></a><a class="group" href="https://github.com/bluesky-social"><span class="sr-only">Follow us on GitHub</span><svg viewBox="0 0 20 20" aria-hidden="true" class="h-5 w-5 fill-zinc-700 transition group-hover:fill-zinc-900 dark:group-hover:fill-zinc-500"><path fill-rule="evenodd" clip-rule="evenodd" d="M10 1.667c-4.605 0-8.334 3.823-8.334 8.544 0 3.78 2.385 6.974 5.698 8.106.417.075.573-.182.573-.406 0-.203-.011-.875-.011-1.592-2.093.397-2.635-.522-2.802-1.002-.094-.246-.5-1.005-.854-1.207-.291-.16-.708-.556-.01-.567.656-.01 1.124.62 1.281.876.75 1.292 1.948.93 2.427.705.073-.555.291-.93.531-1.143-1.854-.213-3.791-.95-3.791-4.218 0-.929.322-1.698.854-2.296-.083-.214-.375-1.09.083-2.265 0 0 .698-.224 2.292.876a7.576 7.576 0 0 1 2.083-.288c.709 0 1.417.096 2.084.288 1.593-1.11 2.291-.875 2.291-.875.459 1.174.167 2.05.084 2.263.53.599.854 1.357.854 2.297 0 3.278-1.948 4.005-3.802 4.219.302.266.563.78.563 1.58 0 1.143-.011 2.061-.011 2.35 0 .224.156.491.573.405a8.365 8.365 0 0 0 4.11-3.116 8.707 8.707 0 0 0 1.567-4.99c0-4.721-3.73-8.545-8.334-8.545Z"></path></svg></a></div></div></footer></div></div></div><script src="/_next/static/chunks/webpack-496a37b6c26ccd6a.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/786ca9324488b5df.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[2972,[\"972\",\"static/chunks/972-f3553dcbba031b91.js\",\"135\",\"static/chunks/135-8f28b0a70a96c388.js\",\"212\",\"static/chunks/212-67e821188dcd5fb8.js\",\"157\",\"static/chunks/app/%5Blocale%5D/specs/oauth/page-83f2d22457027977.js\"],\"\"]\n5:I[2510,[\"972\",\"static/chunks/972-f3553dcbba031b91.js\",\"135\",\"static/chunks/135-8f28b0a70a96c388.js\",\"212\",\"static/chunks/212-67e821188dcd5fb8.js\",\"157\",\"static/chunks/app/%5Blocale%5D/specs/oauth/page-83f2d22457027977.js\"],\"Code\"]\n6:I[1684,[\"972\",\"static/chunks/972-f3553dcbba031b91.js\",\"135\",\"static/chunks/135-8f28b0a70a96c388.js\",\"212\",\"static/chunks/212-67e821188dcd5fb8.js\",\"157\",\"static/chunks/app/%5Blocale%5D/specs/oauth/page-83f2d22457027977.js\"],\"Heading\"]\n7:I[4707,[],\"\"]\n9:I[6423,[],\"\"]\nc:I[1060,[],\"\"]\n8:[\"locale\",\"en\",\"d\"]\nd:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"UWK7gfTTdjb8rbg3vyV6o\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"specs\",\"oauth\"],\"initialTree\":[\"\",{\"children\":[[\"locale\",\"en\",\"d\"],{\"children\":[\"specs\",{\"children\":[\"oauth\",{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",true]}],\"initialSeedData\":[\"\",{\"children\":[[\"locale\",\"en\",\"d\"],{\"children\":[\"specs\",{\"children\":[\"oauth\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",[\"$\",\"article\",null,{\"className\":\"flex h-full flex-col pb-24 pt-16\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex-auto prose dark:prose-invert [html_:where(\u0026\u003e*)]:mx-auto [html_:where(\u0026\u003e*)]:max-w-2xl [html_:where(\u0026\u003e*)]:lg:mx-[calc(50%-min(50%,theme(maxWidth.lg)))] [html_:where(\u0026\u003e*)]:lg:max-w-3xl\",\"children\":[[\"$\",\"h1\",null,{\"children\":\"OAuth\"}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"my-6 flex gap-2.5 rounded-2xl border border-blue-500/20 bg-blue-50/50 p-4 leading-6 text-blue-900 dark:border-blue-500/30 dark:bg-blue-500/5 dark:text-blue-200 dark:[--tw-prose-links-hover:theme(colors.blue.300)] dark:[--tw-prose-links:theme(colors.white)]\",\"children\":[[\"$\",\"svg\",null,{\"viewBox\":\"0 0 16 16\",\"aria-hidden\":\"true\",\"className\":\"mt-1 h-4 w-4 flex-none fill-blue-500 stroke-white dark:fill-blue-200/20 dark:stroke-blue-200\",\"children\":[[\"$\",\"circle\",null,{\"cx\":\"8\",\"cy\":\"8\",\"r\":\"8\",\"strokeWidth\":\"0\"}],[\"$\",\"path\",null,{\"fill\":\"none\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"strokeWidth\":\"1.5\",\"d\":\"M6.75 7.75h1.5v3.5\"}],[\"$\",\"circle\",null,{\"cx\":\"8\",\"cy\":\"4\",\"r\":\".5\",\"fill\":\"none\"}]]}],[\"$\",\"div\",null,{\"className\":\"[\u0026\u003e:first-child]:mt-0 [\u0026\u003e:last-child]:mb-0\",\"children\":[\"$\",\"p\",null,{\"children\":[\"The OAuth profile for atproto is new and may be revised based on feedback from the development community and ongoing standards work. Read more about the rollout in the \",[\"$\",\"$L4\",null,{\"href\":\"https://github.com/bluesky-social/atproto/discussions/2656\",\"children\":\"OAuth Roadmap\"}],\".\"]}]}]]}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"my-6 flex gap-2.5 rounded-2xl border border-blue-500/20 bg-blue-50/50 p-4 leading-6 text-blue-900 dark:border-blue-500/30 dark:bg-blue-500/5 dark:text-blue-200 dark:[--tw-prose-links-hover:theme(colors.blue.300)] dark:[--tw-prose-links:theme(colors.white)]\",\"children\":[[\"$\",\"svg\",null,{\"viewBox\":\"0 0 16 16\",\"aria-hidden\":\"true\",\"className\":\"mt-1 h-4 w-4 flex-none fill-blue-500 stroke-white dark:fill-blue-200/20 dark:stroke-blue-200\",\"children\":[[\"$\",\"circle\",null,{\"cx\":\"8\",\"cy\":\"8\",\"r\":\"8\",\"strokeWidth\":\"0\"}],[\"$\",\"path\",null,{\"fill\":\"none\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"strokeWidth\":\"1.5\",\"d\":\"M6.75 7.75h1.5v3.5\"}],[\"$\",\"circle\",null,{\"cx\":\"8\",\"cy\":\"4\",\"r\":\".5\",\"fill\":\"none\"}]]}],[\"$\",\"div\",null,{\"className\":\"[\u0026\u003e:first-child]:mt-0 [\u0026\u003e:last-child]:mb-0\",\"children\":[\"$\",\"p\",null,{\"children\":[\"This specification is authoritative, but is not an implementation guide and does not provide much background or context around design decisions. The earlier \",[\"$\",\"$L4\",null,{\"href\":\"https://github.com/bluesky-social/proposals/tree/main/0004-oauth\",\"children\":\"design proposal\"}],\" is not authoritative but provides more context and examples. SDK documentation and the \",[\"$\",\"$L4\",null,{\"href\":\"https://docs.bsky.app/docs/advanced-guides/oauth-client\",\"children\":\"client implementation guide\"}],\" are more approachable for developers.\"]}]}]]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"OAuth is the primary mechanism in atproto for clients to make authorized requests to PDS instances. Most user-facing software is expected to use OAuth, including \\\"front-end\\\" clients like mobile apps, rich browser apps, or native desktop apps, as well as \\\"back-end\\\" clients like web services.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"See the \",[\"$\",\"$L4\",null,{\"href\":\"./xrpc\",\"children\":\"HTTP API specification\"}],\" for other forms of auth in atproto, including legacy HTTP client sessions/tokens, and inter-service auth.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"OAuth is a constantly evolving framework of standards and best practices, standardized by the IETF. atproto uses a specific \\\"profile\\\" of OAuth which mandates a particular combination of OAuth standards, as described in this document.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"At a high level, we start with the \\\"OAuth 2.1\\\" (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/\",\"children\":[\"$\",\"$L5\",null,{\"children\":\"draft-ietf-oauth-v2-1\"}]}],\") evolution of OAuth 2.0, which means:\"]}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":\"only the \\\"authorization code\\\" OAuth 2.0 grant type is supported, not \\\"implicit\\\" or other grant types\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"mandatory Proof Key for Code Exchange (PKCE, \",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/rfc7636\",\"children\":\"RFC 7636\"}],\")\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"security best practices (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics\",\"children\":[\"$\",\"$L5\",null,{\"children\":\"draft-ietf-oauth-security-topics\"}]}],\" and \",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps\",\"children\":[\"$\",\"$L5\",null,{\"children\":\"draft-ietf-oauth-browser-based-apps\"}]}],\") are required\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Unlike a centralized app platform, in atproto there are many independent server implementations, so server discovery and client registration are automated using a combination of public auth server metadata and public client metadata. The \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" is a fully-qualified web URL pointing to the public client metadata (JSON document). There is no \",[\"$\",\"$L5\",null,{\"children\":\"client_secret\"}],\" shared between servers and clients. When initiating a login with a handle or DID, an atproto-specific identity resolution step is required to discover the account’s PDS network location.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"In OAuth terminology, an atproto Personal Data Server (PDS) is a \\\"Resource Server\\\" to which authorized HTTP requests are made using access tokens. Sometimes the PDS is also the \\\"Authorization Server\\\" - which services OAuth authorization flows and token requests - while in other situations a separate \\\"entryway\\\" service acts as the Authorization Server for multiple PDS instances. Clients from a metadata file from the PDS to discover the Authorization Server network location.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"DPoP (with mandatory server issued nonces) is required to bind auth tokens to specific client software instances (eg, end devices or browser sessions). Pushed Authentication Requests (PAR) are used to streamline the authorization request flow. \\\"Confidential\\\" clients use JWTs signed with a secret key to authenticate the client software to Authorization Servers when making authorization requests.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Automated client registration using client metadata is one of the more novel aspects of OAuth in atproto. As of August 2024, client metadata is still an Internet Draft (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document/\",\"children\":[\"$\",\"$L5\",null,{\"children\":\"draft-parecki-oauth-client-id-metadata-document\"}]}],\"); it should not be confused with the existing \\\"Dynamic Client Registration\\\" standard (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/rfc7591\",\"children\":\"RFC 7591\"}],\"). We are hopeful other open protocols will adopt similar automated registration flows in the future, but there may not be general OAuth ecosystem support for some time.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"OAuth 2.0 is traditionally an authorization (\",[\"$\",\"$L5\",null,{\"children\":\"authz\"}],\") system, not an authentication (\",[\"$\",\"$L5\",null,{\"children\":\"authn\"}],\") system, meaning that it is not always a solution for pure account authentication use cases, such as \\\"Signup/Login with XYZ\\\" identity integrations. OpenID Connect (OIDC), which builds on top of OAuth 2.0, is usually the recommended standard for identity authentication. Unfortunately, the current version of OIDC does not enable authentication of atproto identities in a secure and generic way. The atproto profile of OAuth includes a (mandatory) mechanism for account authentication during the authorization flow and can be used for atproto identity authentication use cases.\"]}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"clients\",\"children\":\"Clients\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"This section describes requirements for OAuth clients, which are enforced by Authorization Servers.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"OAuth client software is identified by a globally unique \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\". Distinct variants of client software may have distinct \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" values; for example the browser app and Android (mobile OS) variants of the same software might have different \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" values. As required by the \",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document\",\"children\":[\"$\",\"$L5\",null,{\"children\":\"draft-parecki-oauth-client-id-metadata-document\"}]}],\" specification draft, the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" must be a fully-qualified web URL from which the client-metadata JSON document can be fetched. For example, \",[\"$\",\"$L5\",null,{\"children\":\"https://app.example.com/client-metadata.json\"}],\". Some more about the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\":\"]}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":\"it must be a well-formed URL, following the W3C URL specification\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"the schema must be \",[\"$\",\"$L5\",null,{\"children\":\"https://\"}],\", and there must not be a port number included. Note that there is a special exception for \",[\"$\",\"$L5\",null,{\"children\":\"http://localhost\"}],\" \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" values for development, see details below\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"the path does not need to include \",[\"$\",\"$L5\",null,{\"children\":\"client-metadata.json\"}],\", but it is helpful convention\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Authorization Servers which support both the atproto OAuth profile and other forms of OAuth should take care to prevent \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" value collisions. For example, \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" values for clients which are not auto-registered should never have the prefix \",[\"$\",\"$L5\",null,{\"children\":\"https://\"}],\" or \",[\"$\",\"$L5\",null,{\"children\":\"http://\"}],\".\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Types of Clients\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"All atproto OAuth clients need to meet a core set of standards and requirements, but there are a few variations in capabilities (such as session lifetime) depending on the security properties of the client itself.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"As described in the OAuth 2.0 specification (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/rfc6749\",\"children\":\"RFC 6749\"}],\"), every client is one of two broad types:\"]}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":\"confidential clients\"}],\" are clients which can authenticate themselves to Authorization Servers using a cryptographic signing key. This allows refresh tokens to be bound to the specific client. Note that this form of client authentication is distinct from DPoP: the client authentication key is common to all client sessions (although it can be rotated). This usually means that there is a web service controlled by the client which holds the key. Because they are authenticated and can revoke tokens in a security incident, confidential clients may be trusted with longer session and token lifetimes.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":\"public clients\"}],\" do not authenticate using a client signing key, either because they don’t have a server-side component (the client software all runs on end-user devices), or they simply chose not to implement it.\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"It is acceptable for a web service to act as a public client, and conversely it is possible for mobile apps and browser apps to coordinate with a token-mediating backend service and for the combination to form a confidential client. Mobile apps and browser apps can also adopt a \\\"backend-for-frontend\\\" (BFF) architecture with a web service backend acting as the OAuth client. This document will use the \\\"public\\\" vs \\\"confidential\\\" client terminology for clarity.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The environment a client runs in also impacts the type of redirect (callback) URLs it uses during the Authorization Flow:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":\"web clients\"}],\" include web services and browser apps. Redirect URLs are regular web URLs which open in a browser.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":\"native clients\"}],\" include some mobile and desktop native clients. Redirect URLs may use platform-specific app callback schemes to open in the app itself.\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Authorization Servers may maintain a set of \\\"trusted\\\" clients, identified by \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\". Because any client could use unverified client metadata to impersonate a better-known app or brand, Authorization Servers should not display such metadata to end users in the Authorization Interface by default. Trusted clients can have additional metadata shown, such as a readable name (\",[\"$\",\"$L5\",null,{\"children\":\"client_name\"}],\"), project URI (\",[\"$\",\"$L5\",null,{\"children\":\"client_uri\"}],\", which may have a different domain/origin than \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\") and logo (\",[\"$\",\"$L5\",null,{\"children\":\"logo_uri\"}],\"). See the \\\"Security Considerations\\\" section for more details.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Clients which are only using atproto OAuth for account authentication (without authorization to access PDS resources) should request minimal scopes (see \\\"Scopes\\\" section), but still need to implement most of the authorization flow. In particular, it is critical that they check the \",[\"$\",\"$L5\",null,{\"children\":\"sub\"}],\" field in a token response to verify the account identity (this is an atproto-specific detail).\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Client ID Metadata Document\"}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"my-6 flex gap-2.5 rounded-2xl border border-blue-500/20 bg-blue-50/50 p-4 leading-6 text-blue-900 dark:border-blue-500/30 dark:bg-blue-500/5 dark:text-blue-200 dark:[--tw-prose-links-hover:theme(colors.blue.300)] dark:[--tw-prose-links:theme(colors.white)]\",\"children\":[[\"$\",\"svg\",null,{\"viewBox\":\"0 0 16 16\",\"aria-hidden\":\"true\",\"className\":\"mt-1 h-4 w-4 flex-none fill-blue-500 stroke-white dark:fill-blue-200/20 dark:stroke-blue-200\",\"children\":[[\"$\",\"circle\",null,{\"cx\":\"8\",\"cy\":\"8\",\"r\":\"8\",\"strokeWidth\":\"0\"}],[\"$\",\"path\",null,{\"fill\":\"none\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"strokeWidth\":\"1.5\",\"d\":\"M6.75 7.75h1.5v3.5\"}],[\"$\",\"circle\",null,{\"cx\":\"8\",\"cy\":\"4\",\"r\":\".5\",\"fill\":\"none\"}]]}],[\"$\",\"div\",null,{\"className\":\"[\u0026\u003e:first-child]:mt-0 [\u0026\u003e:last-child]:mb-0\",\"children\":[\"$\",\"p\",null,{\"children\":[\"The Client ID Metadata Document specification (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document/\",\"children\":[\"$\",\"$L5\",null,{\"children\":\"draft-parecki-oauth-client-id-metadata-document\"}]}],\" is still a draft and may evolve over time. Our intention is to evolve and align with subsequent drafts and any final standard, while minimizing disruption and breakage with existing implementations.\"]}]}]]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Clients must publish a \\\"client metadata\\\" JSON file on the public web. This will be fetched dynamically by Authorization Servers as part of the authorization request (PAR) and at other times during the session lifecycle. The response HTTP status must be 200 (not another 2xx or a redirect), with a JSON object body with the correct \",[\"$\",\"$L5\",null,{\"children\":\"Content-Type\"}],\" (\",[\"$\",\"$L5\",null,{\"children\":\"application/json\"}],\").\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Authorization Servers need to fetch client metadata documents from the public web. They should use a hardened HTTP client for these requests (see \\\"OAuth Security Considerations\\\"). Servers may cache client metadata responses, optionally respecting HTTP caching headers (within limits). Minimum and maximum cache TTLs are not currently specified, but should be chosen to ensure that auth token requests predicated on stale confidential client authentication keys (\",[\"$\",\"$L5\",null,{\"children\":\"jwks\"}],\" or \",[\"$\",\"$L5\",null,{\"children\":\"jwks_uris\"}],\") are rejected in a timely manner.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The following fields are relevant for all client types:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" (string, required): the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\". Must exactly match the full URL used to fetch the client metadata file itself\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"application_type\"}],\" (string, optional): must be one of \",[\"$\",\"$L5\",null,{\"children\":\"web\"}],\" or \",[\"$\",\"$L5\",null,{\"children\":\"native\"}],\", with \",[\"$\",\"$L5\",null,{\"children\":\"web\"}],\" as the default if not specified. Note that this is field specified by OpenID/OIDC, which we are borrowing. Used by the Authorization Server to enforce the relevant \\\"best current practices\\\".\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"grant_types\"}],\" (array of strings, required): \",[\"$\",\"$L5\",null,{\"children\":\"authorization_code\"}],\" must always be included. \",[\"$\",\"$L5\",null,{\"children\":\"refresh_token\"}],\" is optional, but must be included if the client will make token refresh requests.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"scope\"}],\" (string, sub-strings space-separated, required): all scope values which \",[\"$\",\"em\",null,{\"children\":\"might\"}],\" be requested by this client are declared here. The \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\" scope is required, so must be included here. See \\\"Scopes\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"response_types\"}],\" (array of strings, required): \",[\"$\",\"$L5\",null,{\"children\":\"code\"}],\" must be included.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"redirect_uris\"}],\" (array of strings, required): at least one redirect URI is required. See Authorization Request Fields section for rules about redirect URIs, which also apply here.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"token_endpoint_auth_method\"}],\" (string, optional): confidential clients must set this to \",[\"$\",\"$L5\",null,{\"children\":\"private_key_jwt\"}],\".\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"token_endpoint_auth_signing_alg\"}],\" (string, optional): \",[\"$\",\"$L5\",null,{\"children\":\"none\"}],\" is never allowed here. The current recommended and most-supported algorithm is \",[\"$\",\"$L5\",null,{\"children\":\"ES256\"}],\", but this may evolve over time. Authorization Servers will compare this against their supported algorithms.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"dpop_bound_access_tokens\"}],\" (boolean, required): DPoP is mandatory for all clients, so this must be present and \",[\"$\",\"$L5\",null,{\"children\":\"true\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"jwks\"}],\" (object with array of JWKs, optional): confidential clients must supply at least one public key in JWK format for use with JWT client authentication. Either this field or the \",[\"$\",\"$L5\",null,{\"children\":\"jwks_uri\"}],\" field must be provided for confidential clients, but not both.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"jwks_uri\"}],\" (string, optional): URL pointing to a JWKS JSON object. See \",[\"$\",\"$L5\",null,{\"children\":\"jwks\"}],\" above for details.\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"These fields are optional but recommended:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_name\"}],\" (string, optional): human-readable name of the client\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_uri\"}],\" (string, optional): not to be confused with \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\", this is a homepage URL for the client. If provided, the \",[\"$\",\"$L5\",null,{\"children\":\"client_uri\"}],\" must have the same hostname as \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\".\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"logo_uri\"}],\" (string, optional): URL to client logo. Only \",[\"$\",\"$L5\",null,{\"children\":\"https:\"}],\" URIs are allowed.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"tos_uri\"}],\" (string, optional): URL to human-readable terms of service (ToS) for the client. Only \",[\"$\",\"$L5\",null,{\"children\":\"https:\"}],\" URIs are allowed.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"policy_uri\"}],\" (string, optional): URL to human-readable privacy policy for the client. Only \",[\"$\",\"$L5\",null,{\"children\":\"https:\"}],\" URIs are allowed.\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"See \\\"OAuth Security Considerations\\\" below for when \",[\"$\",\"$L5\",null,{\"children\":\"client_name\"}],\", \",[\"$\",\"$L5\",null,{\"children\":\"client_uri\"}],\", and \",[\"$\",\"$L5\",null,{\"children\":\"logo_uri\"}],\" will or will not be displayed to end users.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Additional optional client metadata fields are enumerated with \",[\"$\",\"$L4\",null,{\"href\":\"https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata\",\"children\":\"IANA\"}],\". Note that these are shared with the \\\"Dynamic Client Registration\\\" standard, which is not used directly by the atproto OAuth profile.\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Localhost Client Development\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"When working with a developent environment (Authorization Server and Client), it may be difficult for developers to publish in-progress client metadata at a public URL so that authorization servers can access it. This may even be true for development environments using a containerized Authorization Server and local DNS, because of SSRF protections against local IP ranges.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"To make development workflows easier, a special exception is made for clients with \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" having origin \",[\"$\",\"$L5\",null,{\"children\":\"http://localhost\"}],\" (with no port number specified). Authorization Servers are encouraged to support this exception - including in production environments - but it is optional.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"In a localhost \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" scenario, the Authorization Server should verify that the scheme is \",[\"$\",\"$L5\",null,{\"children\":\"http\"}],\", and that the hostname is exactly \",[\"$\",\"$L5\",null,{\"children\":\"localhost\"}],\" with no port specified. IP addresses (\",[\"$\",\"$L5\",null,{\"children\":\"127.0.0.1\"}],\", etc) are not supported. The path parameter must be empty (\",[\"$\",\"$L5\",null,{\"children\":\"/\"}],\").\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"In the Authorization Request, the \",[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\" must match one of those supplied (or a default). Path components must match, but port numbers are not matched.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Some metadata fields can be configured via query parameter in the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" URL (with appropriate urlencoding):\"]}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\" (string, multiple query parameters allowed, optional): allows declaring a local redirect/callback URL, with path component matched but port numbers ignored. The default values (if none are supplied) are \",[\"$\",\"$L5\",null,{\"children\":\"http://127.0.0.1/\"}],\" and \",[\"$\",\"$L5\",null,{\"children\":\"http://[::1]/\"}],\".\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"scope\"}],\" (string with space-separated values, single query parameter allowed, optional): the set of scopes which might be requested by the client. Default is \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\".\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The other parameters in the virtual client metadata document will be:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" (string): the exact \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" (URL) used to generate the virtual document\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_name\"}],\" (string): a value chosen by the Authorization Server (e.g. \\\"Development client\\\")\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"response_types\"}],\" (array of strings): must include \",[\"$\",\"$L5\",null,{\"children\":\"code\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"grant_types\"}],\" (array of strings): \",[\"$\",\"$L5\",null,{\"children\":\"authorization_code\"}],\" and \",[\"$\",\"$L5\",null,{\"children\":\"refresh_token\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"token_endpoint_auth_method\"}],\": \",[\"$\",\"$L5\",null,{\"children\":\"none\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"application_type\"}],\": \",[\"$\",\"$L5\",null,{\"children\":\"native\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"dpop_bound_access_tokens\"}],\": \",[\"$\",\"$L5\",null,{\"children\":\"true\"}]]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Note that this works as a public client, not a confidential client.\"}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"identity-authentication\",\"children\":\"Identity Authentication\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"As mentioned in the introduction, OAuth 2.0 generally provides only Authorization (\",[\"$\",\"$L5\",null,{\"children\":\"authz\"}],\"), and additional standards like OpenID/OIDC are used for Authentication (\",[\"$\",\"$L5\",null,{\"children\":\"authn\"}],\"). The atproto profile of OAuth requires authentication of account identity and supports the use case of simple identity authentication without additional resource access authorization.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"In atproto, account identity is anchored in the account DID, which is the permanent, globally unique, publicly resolvable identifier for the account. The DID resolves to a DID document which indicates the current PDS host location for the account. That PDS (combined with an optional entryway) is the authorization authority and the OAuth Authorization Server for the account. When speaking to any Authorization Server, it is critical (mandatory) for clients to confirm that it is actually the authoritative server for the account in question, which means independently resolving the account identity (by DID) and confirming that the Authorization Server matches. It is also critical (mandatory) to confirm at the end of an authorization flow that the Authorization Server actually authorized the expected account. The reason this is necessary is to confirm that the Authorization Server is authoritative for the account in question. Otherwise a malicious server could authenticate arbitrary accounts (DIDs) to the client.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Clients can start an auth flow in one of two ways:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":\"starting with a public account identifier, provided by the user: handle or DID\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"starting with a server hostname, provided by the user: PDS or entryway, mapping to either Resource Server and/or Authorization Server\"}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"One use case for starting with a server instead of an account identifier is when the user does not remember their full account handle or only knows their account email. Another is for authentication when a user’s handle is broken. The user will still need to know their hosting provider in these situation.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"When starting with an account identifier, the client must resolve the atproto identity to a DID document. If starting with a handle, it is critical (mandatory) to bidirectionally verify the handle by checking that the DID document claims the handle (see atproto Handle specification). All handle resolution techniques and all atproto-blessed DID methods must be supported to ensure interoperability with all accounts.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"In some client environments, it may be difficult to resolve all identity types. For example, handle resolution may involve DNS TXT queries, which are not directly supported from browser apps. Client implementations might use alternative techniques (such as DNS-over-HTTP) or could make use of a supporting web service to resolve identities.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Because authorization flows are security-critical, any caching of identity resolution should choose cache lifetimes carefully. Cache lifetimes of less than 10 minutes are recommended for auth flows specifically.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The resolved DID should be bound to the overall auth session and should be used as the primary account identifier within client app code. Handles (when verified) are acceptable to display in user interfaces, but may change over time and need to be re-verified periodically. When passing an account identifier through to the Authorization Server as part of the Authorization Request in the \",[\"$\",\"$L5\",null,{\"children\":\"login_hint\"}],\", it is recommended to use the exact account identifier supplied by the user (handle or DID) to ensure any sign-in flow is consistent (users might not recognize their own account DID).\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"At the end of the auth flow, when the client does an initial token fetch, the Authorization Server must return the account DID in the \",[\"$\",\"$L5\",null,{\"children\":\"sub\"}],\" field of the JSON response body. If the entire auth flow started with an account identifier, it is critical for the client to verify that this DID matches the expected DID bound to the session earlier; the linkage from account to Authorization Server will already have been verified in this situation.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"If the auth flow instead starts with a server (hostname or URL), the client will first attempt to fetch Resource Server metadata (and resolve to Authorization Server if found) and then attempt to fetch Authorization Server metadata. See \\\"Authorization Server\\\" section for server metadata fetching. If either is successful, the client will end up with an identified Authorization Server. The Authorization Request flow will proceed without a \",[\"$\",\"$L5\",null,{\"children\":\"login_hint\"}],\" or account identifier being bound to the session, but the Authorization Server \",[\"$\",\"$L5\",null,{\"children\":\"issuer\"}],\" will be bound to the session.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"After the auth flow continues and an initial token request succeeds, the client will parse the account identifier from the \",[\"$\",\"$L5\",null,{\"children\":\"sub\"}],\" field in the token response. At this point, the client still cannot trust that it has actually authenticated the indicated account. It is critical for the client to resolve the identity (DID document), extract the declared PDS host, confirm that the PDS (Resource Server) resolves to the Authorization Server bound to the session by fetching the Resource Server metadata, and fetch the Authorization Server metadata to confirm that the \",[\"$\",\"$L5\",null,{\"children\":\"issuer\"}],\" field matches the Authorization Server origin (see \",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-7.13.1\",\"children\":[[\"$\",\"$L5\",null,{\"children\":\"draft-ietf-oauth-v2-1\"}],\" section 7.3.1\"]}],\" regarding this last point).\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"To reiterate, it is critical for all clients - including those only interested in atproto Identity Authentication - to go through the entire Authorization flow and to verify that the account identifier (DID) in the \",[\"$\",\"$L5\",null,{\"children\":\"sub\"}],\" field of the token response is consistent with the Authorization Server hostname/origin (\",[\"$\",\"$L5\",null,{\"children\":\"issuer\"}],\").\"]}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"authorization-scopes\",\"children\":\"Authorization Scopes\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"OAuth scopes allow more granular control over the resources and actions a client is granted access to.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The special \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\" scope is required for all atproto OAuth sessions. The semantics are somewhat similar to the \",[\"$\",\"$L5\",null,{\"children\":\"openid\"}],\" scope: inclusion of it confirms that the client is using the atproto profile of OAuth and will comply with all the requirements laid out in this specification. No access to any atproto-specific PDS resources will be granted without this scope included.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Authorization Servers may support other profiles of OAuth if client does not include the \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\" scope. For example, an Authorization Server might function as both an atproto PDS/entryway, and support other protocols/standards at the same time.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Use of the atproto OAuth profile, as indicated by the \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\" scope, means that the Authorization Server will return the atproto account DID as an account identifier in the \",[\"$\",\"$L5\",null,{\"children\":\"sub\"}],\" field of token requests. Authorization Servers must return \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\" in \",[\"$\",\"$L5\",null,{\"children\":\"scopes_supported\"}],\" in their metadata document, so that clients know they support the atproto OAuth profile. A client may include only the \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\" scope if they only need account authentication - for example a \\\"Login with atproto\\\" use case. Unlike OpenID, profile metadata in atproto is generally public, so an additional authorization scope for fetching profile metadata is not needed.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The OAuth 2.0 specification does not require Authorization Servers to return the granted scopes in the token responses unless the scope that was granted is different from what the client requested. In the atproto OAuth profile, servers must always return the granted scopes in the token response. Clients should reject token responses if they don't contain a \",[\"$\",\"$L5\",null,{\"children\":\"scope\"}],\" field, or if the \",[\"$\",\"$L5\",null,{\"children\":\"scope\"}],\" field does not contain \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\".\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The intention is to support flexible scopes based on Lexicon namespaces (NSIDs) so that clients can be given access only to the specific content and API endpoints they need access to. Until the design of that scope system is ready, the atproto profile of OAuth defines two transitional scopes which align with the permissions granted under the original \\\"session token\\\" auth system:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"transition:generic\"}],\": broad PDS account permissions, equivalent to the previous \\\"App Password\\\" authorization level.\",\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":\"write (create/update/delete) any repository record type\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"upload blobs (media files)\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"read and write any personal preferences\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"API endpoints and service proxying for most Lexicon endpoints, to any service provider (identified by DID)\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"ability to generate service auth tokens for the specific API endpoints the client has access to\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"no account management actions: change handle, change email, delete or deactivate account, migrate account\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"no access to DMs (the \",[\"$\",\"$L5\",null,{\"children\":\"chat.bsky.*\"}],\" Lexicons), specifically\"]}],\"\\n\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"transition:chat.bsky\"}],\": equivalent to adding the \\\"DM Access\\\" toggle for \\\"App Passwords\\\"\",\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[\"API endpoints and service proxying for the \",[\"$\",\"$L5\",null,{\"children\":\"chat.bsky\"}],\" Lexicons specifically\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"ability to generate service auth tokens for the \",[\"$\",\"$L5\",null,{\"children\":\"chat.bsky\"}],\" Lexicons\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"this scope depends on and does not function without the \",[\"$\",\"$L5\",null,{\"children\":\"transition:generic\"}],\" scope\"]}],\"\\n\"]}],\"\\n\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"authorization-requests\",\"children\":\"Authorization Requests\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"This section details standards and requirements specific to Authorization Requests.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"PKCE and PAR are required for all client types and Authorization Servers. Confidential clients authenticate themselves using JWT client assertions.\"}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Request Fields\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"A summary of fields relevant to authorization requests with the atproto OAuth profile:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" (string, required): identifies the client software. See \\\"Clients\\\" section above for details.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"response_type\"}],\" (string, required): must be \",[\"$\",\"$L5\",null,{\"children\":\"code\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"code_challenge\"}],\" (string, required): the PKCE challenge value. See \\\"PKCE\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"code_challenge_method\"}],\" (string, required): which code challenge method is used, for example \",[\"$\",\"$L5\",null,{\"children\":\"S256\"}],\". See \\\"PKCE\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"state\"}],\" (string, required): random token used to verify the authorization request against the response. See below.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\" (string, required): must match against URIs declared in client metadata and have a format consistent with the \",[\"$\",\"$L5\",null,{\"children\":\"application_type\"}],\" declared in the client metadata. See below.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"scope\"}],\" (string with space-separated values, required): must be a subset of the scopes declared in client metadata. Must include \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\". See \\\"Scopes\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_assertion_type\"}],\" (string, optional): used by confidential clients to describe the client authentication mechanism. See \\\"Confidential Client\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_assertion\"}],\" (string, optional): only used for confidential clients, for client authentication. See \\\"Confidential Client\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"login_hint\"}],\" (string, optional): account identifier to be used for login. See \\\"Authorization Interface\\\" section.\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The \",[\"$\",\"$L5\",null,{\"children\":\"client_secret\"}],\" value, used in many other OAuth profiles, should not be included.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The \",[\"$\",\"$L5\",null,{\"children\":\"state\"}],\" parameter in client authorization requests is mandatory. Clients should use randomly-generated tokens for this parameter and not have collisions or reuse tokens across any combination of device, account, or session. Authorization Servers should reject duplicate state parameters, but are not currently required to track state values across accounts or sessions. The \",[\"$\",\"$L5\",null,{\"children\":\"state\"}],\" parameter is effectively used to verify the \",[\"$\",\"$L5\",null,{\"children\":\"issuer\"}],\" later, and it is important that the parameter can not be forged or guessed by an untrusted party.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"For web clients, the \",[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\" is a HTTPS URL which will be redirected in the browser to return users to the application at the end of the Authorization flow. The URL may include a port number, but not if it is the default port number. The \",[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\" must match one of the URIs declared in the client metadata and the Authorization Server must verify this condition. The URL origin must match that of the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\".\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"There is a special exception for the localhost development workflow to use \",[\"$\",\"$L5\",null,{\"children\":\"http://127.0.0.1\"}],\" or \",[\"$\",\"$L5\",null,{\"children\":\"http://[::1]\"}],\" URLs, with matching rules described in the \\\"Localhost Client Development\\\" section. These clients use web URLs, but have \",[\"$\",\"$L5\",null,{\"children\":\"application_type\"}],\" set to \",[\"$\",\"$L5\",null,{\"children\":\"native\"}],\" in the generated client metadata.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"For native clients, the \",[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\" may use a custom URI scheme to have the operating system redirect the user back to the app, instead of a web browser. Native clients are also allowed to use an HTTPS URL. Any custom scheme must match the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" hostname in reverse-domain order. The URI scheme must be followed by a single colon (\",[\"$\",\"$L5\",null,{\"children\":\":\"}],\") then a single forward slash (\",[\"$\",\"$L5\",null,{\"children\":\"/\"}],\") and then a URI path component. For example, an app with \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" \",[\"$\",\"$L4\",null,{\"href\":\"https://app.example.com/client-metadata.json\",\"children\":[\"$\",\"$L5\",null,{\"children\":\"https://app.example.com/client-metadata.json\"}]}],\" could have a \",[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\" of \",[\"$\",\"$L5\",null,{\"children\":\"com.example.app:/callback\"}],\".\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Native clients are also allowed to use an HTTPS URL. In this case, the URL origin must be the same as the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\". One example use-case is \\\"Apple Universal Links\\\".\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Clients may include additional optional authorization request parameters - and servers may process them - but they are not required to. Refer to other OAuth standards and the \",[\"$\",\"$L4\",null,{\"href\":\"https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml\",\"children\":\"IANA OAuth parameter registry\"}],\".\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Proof Key for Code Exchange (PKCE)\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"PKCE is mandatory for all Authorization Requests. Clients must generate new, unique, random challenges for every authorization request. Authorization Servers must prevent reuse of \",[\"$\",\"$L5\",null,{\"children\":\"code_challenge\"}],\" values across sessions (at least within some reasonable time frame, such as a 24 hour period).\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The \",[\"$\",\"$L5\",null,{\"children\":\"S256\"}],\" challenge method must be supported by all clients and Authorization Servers; see \",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/rfc7636\",\"children\":\"RFC 7636\"}],\" for details. The \",[\"$\",\"$L5\",null,{\"children\":\"plain\"}],\" method is not allowed. Additional methods may in theory be supported if both client and server support them.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Authorization Servers should reject reuse of a \",[\"$\",\"$L5\",null,{\"children\":\"code\"}],\" value, and revoke any outstanding sessions and tokens associated with the earlier use of the \",[\"$\",\"$L5\",null,{\"children\":\"code\"}],\" value.\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Pushed Authorization Requests (PAR)\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Authorization Servers must support PAR and clients of all types must use PAR for Authorization Requests.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Authorization Servers must set \",[\"$\",\"$L5\",null,{\"children\":\"require_pushed_authorization_requests\"}],\" to \",[\"$\",\"$L5\",null,{\"children\":\"true\"}],\" in their server metadata document and include a valid URL in \",[\"$\",\"$L5\",null,{\"children\":\"pushed_authorization_request_endpoint\"}],\". See \",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/rfc9207\",\"children\":\"RFC 9207\"}],\" for requirements on this URL.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Clients make an HTTPS POST request to the \",[\"$\",\"$L5\",null,{\"children\":\"pushed_authorization_request_endpoint\"}],\" URL, with the request parameters in the form-encoded request body. They receive a \",[\"$\",\"$L5\",null,{\"children\":\"request_uri\"}],\" (not to be confused with \",[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\") in the JSON response object. When they redirect the user to the authorization endpoint (\",[\"$\",\"$L5\",null,{\"children\":\"authorization_endpoint\"}],\"), they omit most of the request parameters they already sent and include this \",[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\" along with \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" as query parameters instead.\"]}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"my-6 flex gap-2.5 rounded-2xl border border-blue-500/20 bg-blue-50/50 p-4 leading-6 text-blue-900 dark:border-blue-500/30 dark:bg-blue-500/5 dark:text-blue-200 dark:[--tw-prose-links-hover:theme(colors.blue.300)] dark:[--tw-prose-links:theme(colors.white)]\",\"children\":[[\"$\",\"svg\",null,{\"viewBox\":\"0 0 16 16\",\"aria-hidden\":\"true\",\"className\":\"mt-1 h-4 w-4 flex-none fill-blue-500 stroke-white dark:fill-blue-200/20 dark:stroke-blue-200\",\"children\":[[\"$\",\"circle\",null,{\"cx\":\"8\",\"cy\":\"8\",\"r\":\"8\",\"strokeWidth\":\"0\"}],[\"$\",\"path\",null,{\"fill\":\"none\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"strokeWidth\":\"1.5\",\"d\":\"M6.75 7.75h1.5v3.5\"}],[\"$\",\"circle\",null,{\"cx\":\"8\",\"cy\":\"4\",\"r\":\".5\",\"fill\":\"none\"}]]}],[\"$\",\"div\",null,{\"className\":\"[\u0026\u003e:first-child]:mt-0 [\u0026\u003e:last-child]:mb-0\",\"children\":[\"$\",\"p\",null,{\"children\":\"PAR is a relatively new and less-supported standard, and the requirement to use PAR may be relaxed if it is found to be too onerous a requirement for client implementations. In that case, Authorization Servers would be required to support both PAR and non-PAR requests with PAR being optional for clients.\"}]}]]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Confidential Client Authentication\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Confidential clients authenticate themselves during the Authorization Request using a JWT client assertion. Authorization Servers may grant confidential clients longer token/session lifetimes. See \\\"Tokens\\\" section for more context.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The client assertion type to use is \",[\"$\",\"$L5\",null,{\"children\":\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\"}],\", as described in \\\"JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants\\\" (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/rfc7523\",\"children\":\"RFC 7523\"}],\"). Clients and Authorization Servers currently must support the \",[\"$\",\"$L5\",null,{\"children\":\"ES256\"}],\" cryptographic system. The set of recommended systems/algorithms is expected to evolve over time.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Additional requirements:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[\"confidential clients must publish one or more client authentication keys (public key) in the client metadata. This can be either direct JWK format as JSON in the \",[\"$\",\"$L5\",null,{\"children\":\"jwks\"}],\" field, or as a separate JWKS JSON object on the web linked by a \",[\"$\",\"$L5\",null,{\"children\":\"jwks_uri\"}],\" URL. A \",[\"$\",\"$L5\",null,{\"children\":\"jwks_uri\"}],\" URL must be a valid fully qualified URL with \",[\"$\",\"$L5\",null,{\"children\":\"https://\"}],\" scheme.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"confidential clients should periodically rotate client keys, adding new keys to the JWKS set and using then for new sessions, then removing old keys once they are no longer associated with any active auth sessions\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"confidential clients must include \",[\"$\",\"$L5\",null,{\"children\":\"token_endpoint_auth_method\"}],\" as \",[\"$\",\"$L5\",null,{\"children\":\"private_key_jwt\"}],\" in their client metadata document\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"confidential clients are expected to immediately remove client authentication keys from their client metadata if the key has been leaked or compromised\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"Authorization Servers must bind active auth sessions for confidential clients to the client authentication key used at the start of the session. The server should revoke the session and reject further token refreshes if the client authentication key becomes absent from the client metadata. This means the Authorization Server is expected to periodically re-fetch client metadata.\"}],\"\\n\"]}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"tokens-and-session-lifetime\",\"children\":\"Tokens and Session Lifetime\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Access tokens are used to authorize client requests to the account's PDS (\\\"Resource Server\\\"). From the standpoint of the client they are opaque, but they are often signed JWTs including an expiration time. Depending on the PDS implementation, it may or may not be possible to revoke individual access tokens in the event of a compromise, so they must be restricted to a relatively short lifetime.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Refresh tokens are used to request new tokens (of both types) from the Authorization Server (PDS or entryway). They are also opaque from the standpoint of clients. Auth sessions can be revoked - invalidating the refresh tokens - so they may have a longer lifetime. In the atproto OAuth profile, refresh tokens are generally single-use, with the \\\"new\\\" refresh token replacing that used in the token request. This means client implementations may need locking primitives to prevent concurrent token refresh requests.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"To request refresh tokens, the client must declare \",[\"$\",\"$L5\",null,{\"children\":\"refresh_token\"}],\" as a grant type in their client metadata.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Tokens are always bound to a unique session DPoP key. Tokens must not be shared or reused across client devices. They must also be uniquely bound to the client software (\",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\"). The overall session ends when the access and refresh tokens can no longer be used.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The specific lifetime of sessions, access tokens, and refresh tokens is up to the Authorization Server implementation and may depend on security assessments of client type and reputation.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Some guidelines and requirements:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":\"access token lifetimes should be less than 30 minutes in all situations. If the server cannot revoke individual access tokens then the maximum is 15 minutes, and 5 minutes is recommended.\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"for \\\"untrusted\\\" public clients, overall session lifetime should be limited to 7 days, and the lifetime of individual refresh tokens should be limited to 24 hours\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"for confidential clients, the overall session lifetime may be unlimited. Individual refresh tokens should have a lifetime limited to 180 days\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"confidential clients must use the same client authentication key and assertion method for refresh token requests that they did for the initial authentication request\"}],\"\\n\"]}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"demonstrating-proof-of-possession-d-po-p\",\"children\":\"Demonstrating Proof of Possession (DPoP)\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The atproto OAuth profile mandates use of DPoP for all client types when making auth token requests to the Authorization Server and when making authorized requests to the Resource Server. See \",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/rfc9449\",\"children\":\"RFC 9449\"}],\" for details.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Clients must initiate DPoP in the initial authorization request (PAR).\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Server-provided DPoP nonces are mandatory. The Resource Server and Authorization Server may share nonces (especially if they are the same server) or they may have separate nonces. Clients should track the DPoP nonce per account session and per server. Servers must rotate nonces periodically, with a maximum lifetime of 5 minutes. Servers may use the same nonce across all client sessions and across multiple requests at any point in time. Servers should accept recently-stale (old) nonces to make rotation smoother for clients with multiple concurrent request in-flight. Clients should be resilient to unexpected nonce updates in the form of HTTP 400 errors and should retry those failed requests. Clients must reject responses missing a \",[\"$\",\"$L5\",null,{\"children\":\"DPoP-Nonce\"}],\" header (case insensitive), if the request included DPoP.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Clients must generate and sign a unique DPoP token (JWT) for every request. Each DPoP request JWT must have a unique (randomly generated) \",[\"$\",\"$L5\",null,{\"children\":\"jti\"}],\" nonce. Servers should prevent token replays by tracking \",[\"$\",\"$L5\",null,{\"children\":\"jti\"}],\" nonces and rejecting re-use. They can restrict their client-generated \",[\"$\",\"$L5\",null,{\"children\":\"jti\"}],\" nonce history to the server-generated DPoP nonce so that they do not need to track an endlessly growing set of nonces.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The \",[\"$\",\"$L5\",null,{\"children\":\"ES256\"}],\" (NIST \\\"P-256\\\") cryptographic algorithm must be supported by all clients and servers for DPoP JWT signing. The set of algorithms recommended for use is expected to evolve over time. Clients and Servers may implement additional algorithms and declare them in metadata documents to facilitate cryptographic evolution and negotiation.\"]}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"authorization-servers\",\"children\":\"Authorization Servers\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"To enable browser apps, Authorization Servers must support HTTP CORS requests/headers on relevant endpoints, including server metadata, auth requests (PAR), and token requests.\"}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Server Metadata\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Both Resource Servers (PDS instances) and Authorization Servers (PDS or entryway) need to publish metadata files at well-known HTTPS endpoints.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Resource Server (PDS) metadata must comply with the \\\"OAuth 2.0 Protected Resource Metadata\\\" (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/draft-ietf-oauth-resource-metadata/\",\"children\":[\"$\",\"$L5\",null,{\"children\":\"draft-ietf-oauth-resource-metadata\"}]}],\") draft specification. A summary of requirements:\"]}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[\"the URL path is \",[\"$\",\"$L5\",null,{\"children\":\"/.well-known/oauth-protected-resource\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"response must be an HTTP 200 (not 2xx or redirect), and must be a valid JSON object with content type \",[\"$\",\"$L5\",null,{\"children\":\"application/json\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"must contain an \",[\"$\",\"$L5\",null,{\"children\":\"authorization_servers\"}],\" array of strings, with a single element, which is a fully-qualified URL\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The Authorization Server URL may be the same origin as the Resource Server (PDS), or might point to a separate server (e.g. entryway). The URL must be a simple origin URL: \",[\"$\",\"$L5\",null,{\"children\":\"https\"}],\" scheme, no credentials (user:password), no path, no query string or fragment. A port number is allowed, but a default port (443 for HTTPS) must not be included.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The Authorization Server also publishes metadata, complying with the \\\"OAuth 2.0 Authorization Server Metadata\\\" (\",[\"$\",\"$L4\",null,{\"href\":\"https://datatracker.ietf.org/doc/html/rfc8414\",\"children\":\"RFC 8414\"}],\") standard. A summary of requirements:\"]}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[\"the URL path is \",[\"$\",\"$L5\",null,{\"children\":\"/.well-known/oauth-authorization-server\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"response must be an HTTP 200 (not 2xx or redirect), and must be a valid JSON object with content type \",[\"$\",\"$L5\",null,{\"children\":\"application/json\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"issuer\"}],\" (string, required): the \\\"origin\\\" URL of the Authorization Server. Must be a valid URL, with \",[\"$\",\"$L5\",null,{\"children\":\"https\"}],\" scheme. A port number is allowed (if that matches the origin), but the default port (443 for HTTPS) must not be specified. There must be no path segments. Must match the origin of the URL used to fetch the metadata document itself.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"authorization_endpoint\"}],\" (string, required): endpoint URL for authorization redirects\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"token_endpoint\"}],\" (string, required): endpoint URL for token requests\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"response_types_supported\"}],\" (array of strings, required): must include \",[\"$\",\"$L5\",null,{\"children\":\"code\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"grant_types_supported\"}],\" (array of strings, required): must include \",[\"$\",\"$L5\",null,{\"children\":\"authorization_code\"}],\" and \",[\"$\",\"$L5\",null,{\"children\":\"refresh_token\"}],\" (refresh tokens must be supported)\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"code_challenge_methods_supported\"}],\" (array of strings, required): must include \",[\"$\",\"$L5\",null,{\"children\":\"S256\"}],\" (see \\\"PKCE\\\" section)\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"token_endpoint_auth_methods_supported\"}],\" (array of strings, required): must include both \",[\"$\",\"$L5\",null,{\"children\":\"none\"}],\" (public clients) and \",[\"$\",\"$L5\",null,{\"children\":\"private_key_jwt\"}],\" (confidential clients)\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"token_endpoint_auth_signing_alg_values_supported\"}],\" (array of strings, required): must not include \",[\"$\",\"$L5\",null,{\"children\":\"none\"}],\". Must include \",[\"$\",\"$L5\",null,{\"children\":\"ES256\"}],\" for now. Cryptographic algorithm suites are expected to evolve over time.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"scopes_supported\"}],\" (space-separated string, required): must include \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\". If supporting the transitional grants, they should be included here as well. See \\\"Scopes\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"authorization_response_iss_parameter_supported\"}],\" (boolean): must be \",[\"$\",\"$L5\",null,{\"children\":\"true\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"require_pushed_authorization_requests\"}],\" (boolean): must be \",[\"$\",\"$L5\",null,{\"children\":\"true\"}],\". See \\\"PAR\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"pushed_authorization_request_endpoint\"}],\" (string, required): must be the PAR endpoint URL. See \\\"PAR\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"dpop_signing_alg_values_supported\"}],\" (array of strings, required): currently must include \",[\"$\",\"$L5\",null,{\"children\":\"ES256\"}],\". See \\\"DPoP\\\" section.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"require_request_uri_registration\"}],\" (boolean, optional): default is \",[\"$\",\"$L5\",null,{\"children\":\"true\"}],\"; does not need to be set explicitly, but must not be \",[\"$\",\"$L5\",null,{\"children\":\"false\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"client_id_metadata_document_supported\"}],\" (boolean, required): must be \",[\"$\",\"$L5\",null,{\"children\":\"true\"}],\". See \\\"Client ID Metadata\\\" section.\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The \",[\"$\",\"$L5\",null,{\"children\":\"issuer\"}],\" (\\\"origin\\\") is the overall identifier for the Authorization Server.\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Authorization Interface\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The Authorization Server (PDS/entryway) must implement a web interface for users to authenticate with the server, approve (or reject) Authorization Requests from clients, and manage active sessions. This is called the \\\"Authorization Interface\\\".\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Server implementations can chose their own technology for user authentication and account recovery: secure cookies, email, various two-factor authentication, passkeys, external identity providers (including upstream OpenID/OIDC), etc. Servers may also support multiple concurrent auth sessions with users.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"When a client redirects to the Authorization Server’s authorization URL (the declared \",[\"$\",\"$L5\",null,{\"children\":\"authorization_endpoint\"}],\"), the server first needs to authenticate the user. If there is no active auth session, the user may be prompted to log in. If a \",[\"$\",\"$L5\",null,{\"children\":\"login_hint\"}],\" was provided in the Authorization Request, that can be used to pre-populate the login form. If there are multiple active auth sessions, the user could be prompted to select one from a list, or the \",[\"$\",\"$L5\",null,{\"children\":\"login_hint\"}],\" could be used to auto-select. If there is a single active session, the interface can move to the approval view, possibly with the option to login as a different account. If a \",[\"$\",\"$L5\",null,{\"children\":\"login_hint\"}],\" was supplied, the Authorization Server should only allow the user to authenticate with that account. Otherwise the overall authorization flow will fail when the client verifies the account identity (\",[\"$\",\"$L5\",null,{\"children\":\"sub\"}],\" field).\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The authorization approval prompt should identify the client app and describe the scope of authorization that has been requested.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The amount of client metadata that should be displayed may depend on whether the client is \\\"trusted\\\" by the Authorization Server; see the \\\"Client\\\" and \\\"Security Concerns\\\" sections. The full \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" URL should be displayed by default.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"See the \\\"Scopes\\\" section for a description of scope options.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"If a client is a confidential client and the user has already approved the same scopes for the same client in the past, the Authorization Server may allow \\\"silent sign-in\\\" by auto-approving the request. Authorization Servers can set their own policies for this flow: it may require explicit user configuration, or the client may be required to be \\\"trusted\\\".\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Authorization Servers should separately implement a web interface which allows authenticated users to view active OAuth sessions and delete them.\"}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"summary-of-authorization-flow\",\"children\":\"Summary of Authorization Flow\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"This is a high-level description of what an atproto OAuth authorization flow looks like. It assumes the user already has an atproto account.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The client starts by asking for the user’s account identifier (handle or DID), or for a PDS/entryway hostname. See \\\"Identity Authentication\\\" section for details.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"For an account identifier, the client resolves the identity to a DID document, extracts the declared PDS URL, then fetches the Resource Server and Authorization Server locations. If starting with a server hostname, the client resolves that hostname to an Authorization Server. In either case, Authorization Server metadata is fetched and verified against requirements for atproto OAuth (see \\\"Authorization Server\\\" section).\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The client next makes a Pushed Authorization Request via HTTP POST request. See \\\"Authorization Request\\\" section; some notable details include:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[\"a randomly generated \",[\"$\",\"$L5\",null,{\"children\":\"state\"}],\" token is required, and will be used to reference this authorization request with the subsequent response callback\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"PKCE is required, so a secret value is generated and stored, and a derived challenge is included in the request\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$L5\",null,{\"children\":\"scope\"}],\" values are requested here, and must include \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"for confidential clients, a \",[\"$\",\"$L5\",null,{\"children\":\"client_assertion\"}],\" is included, with type \",[\"$\",\"$L5\",null,{\"children\":\"jwt-bearer\"}],\", signed using the secret client authentication key\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"the client generates a new DPoP key for the user/device/session and uses it starting with the PAR request\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"if the auth flow started with an account identifier, the client should pass that starting identifier via the \",[\"$\",\"$L5\",null,{\"children\":\"login_hint\"}],\" field\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"atproto uses PAR, so the request will be sent as an HTTP POST request to the Authorization Server\"}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The Authorization Server will receive the PAR request and use the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" URL to resolve the client metadata document. The server validates the request and client metadata, then stores information about the session, including binding a DPoP key to the session. The server returns a \",[\"$\",\"$L5\",null,{\"children\":\"request_uri\"}],\" token to the client, including a DPoP nonce via HTTP header.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The client receives the \",[\"$\",\"$L5\",null,{\"children\":\"request_uri\"}],\" and prepares to redirect the user. At this point, the client usually needs to persist information about the session to some type of secure storage, so it can be read back after the redirect returns. This might be a database (for a web service backend) or web platform storage like IndexedDB (for a browser app). The client then redirects the user via browser to the Authorization Server’s auth endpoint, including the \",[\"$\",\"$L5\",null,{\"children\":\"request_uri\"}],\" as a URL parameter. In any case, clients must not store session data directly in the \",[\"$\",\"$L5\",null,{\"children\":\"state\"}],\" request param.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The Authorization Server uses the \",[\"$\",\"$L5\",null,{\"children\":\"request_uri\"}],\" to look up the earlier Authorization Request parameters, authenticates the user (which might include sign-in or account selection), and prompts the user with the Authorization Interface. The user might refine any granular requested scopes, then approves or rejects the request. The Authorization Server redirects the user back to the \",[\"$\",\"$L5\",null,{\"children\":\"redirect_uri\"}],\", which might be a web callback URL, or a native app URI (for native clients).\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The client uses URL query parameters (\",[\"$\",\"$L5\",null,{\"children\":\"state\"}],\" and \",[\"$\",\"$L5\",null,{\"children\":\"iss\"}],\") to look up and verify session information. Using the \",[\"$\",\"$L5\",null,{\"children\":\"code\"}],\" query parameter, the client then makes an initial token request to the Authorization Server’s token endpoint. The client completes the PKCE flow by including the earlier value in the \",[\"$\",\"$L5\",null,{\"children\":\"code_verifier\"}],\" field. Confidential clients need to include a client assertion JWT in the token request; see the \\\"Confidential Client\\\" section. The Authorization Server validates the request and returns a set of tokens, as well as a \",[\"$\",\"$L5\",null,{\"children\":\"sub\"}],\" field indicating the account identifier (DID) for this session, and the \",[\"$\",\"$L5\",null,{\"children\":\"scope\"}],\" that is covered by the issued access token.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"At this point it is critical (mandatory) for all clients to verify that the account identified by the \",[\"$\",\"$L5\",null,{\"children\":\"sub\"}],\" field is consistent with the Authorization Server \\\"issuer\\\" (present in the \",[\"$\",\"$L5\",null,{\"children\":\"iss\"}],\" query string), either by validating against the originally-supplied account DID, or by resolving the accounts DID to confirm the PDS is consistent with the Authorization Server. See \\\"Identity Authentication\\\" section. The Authorization always returns the scopes approved for the session in the \",[\"$\",\"$L5\",null,{\"children\":\"scopes\"}],\" field (even if they are the same as the request, as an atproto OAuth profile requirement), which may reflect partial authorization by the user. Clients must reject the session if the response does not include \",[\"$\",\"$L5\",null,{\"children\":\"atproto\"}],\" in the returned scopes.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Authentication-only clients can end the flow here.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Using the access token, clients are now able to make authorized requests to the PDS (\\\"Resource Server\\\"). They must use DPoP for all such requests, along with the access token. Tokens (both access and refresh) will need to be periodically \\\"refreshed\\\" by subsequent request to the Authorization Server token endpoint. These also require DPoP. See \\\"Tokens and Session Lifetime\\\" section for details.\"}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"security-considerations\",\"children\":\"Security Considerations\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"There are a number of situations where HTTP URLs provided by external parties are fetched by both clients and providers (servers). Care must be taken to prevent harmful fetches due to maliciously crafted URLs, including hostnames which resolve to private or internal IP addresses. The general term for this class of security issue is Server-Side Request Forgery (SSRF). There is also a class of denial-of-service attacks involving HTTP requests to malicious servers, such as huge response bodies, TCP-level slow-loris attacks, etc. We strongly recommend using \\\"hardened\\\" HTTP client implementations/configurations to mitigate these attacks.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Any party can create a client and client metadata file with any contents at any time. Even the hostname in the \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" cannot be entirely trusted to represent the overall client: an untrusted user may have been able to upload the client metadata file to an arbitrary URL on the host. In particular, the \",[\"$\",\"$L5\",null,{\"children\":\"client_uri\"}],\", \",[\"$\",\"$L5\",null,{\"children\":\"client_name\"}],\", and \",[\"$\",\"$L5\",null,{\"children\":\"logo_uri\"}],\" fields are not verified and could be used by a malicious actor to impersonate a legitimate client. It is strongly recommended for Authorization Servers to not display these fields to end users during the auth flow for unknown clients. Service operators may maintain a list of \\\"trusted\\\" \",[\"$\",\"$L5\",null,{\"children\":\"client_id\"}],\" values and display the extra metadata for those apps only.\"]}],\"\\n\",[\"$\",\"$L6\",null,{\"level\":2,\"id\":\"possible-future-changes\",\"children\":\"Possible Future Changes\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Client metadata requests (by the authorization server) might fail for any number of reasons: transient network disruptions, the client server being down for regular maintenance, etc. It seems brittle for the Authorization Server to immediately revoke access to active client sessions in this scenario. Maybe there should be an explicit grace period?\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The requirement that resource server metadata only have a single URL reference to an authorization server might be relaxed.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The details around session and token lifetimes might change with further security review.\"}]]}]}],null],null],null]},[null,[\"$\",\"$L7\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"$8\",\"children\",\"specs\",\"children\",\"oauth\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[null,[\"$\",\"$L7\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"$8\",\"children\",\"specs\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/786ca9324488b5df.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],\"$La\"],null],null]},[null,[\"$\",\"$L7\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$Lb\"],\"globalErrorComponent\":\"$c\",\"missingSlots\":\"$Wd\"}]\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"OAuth - AT Protocol\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"OAuth for Client/Server Authentication and Authorization\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:title\",\"content\":\"OAuth - AT Protocol\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:description\",\"content\":\"OAuth for Client/Server Authentication and Authorization\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:url\",\"content\":\"https://atproto.com/\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:site_name\",\"content\":\"AT Protocol\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:image\",\"content\":\"https://atproto.com/default-social-card.png\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:image:secure_url\",\"content\":\"https://atproto.com/default-social-card.png\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"13\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"14\",{\"name\":\"twitter:title\",\"content\":\"OAuth - AT Protocol\"}],[\"$\",\"meta\",\"15\",{\"name\":\"twitter:description\",\"content\":\"OAuth for Client/Server Authentication and Authorization\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:image\",\"content\":\"https://atproto.com/default-social-card.png\"}]]\n3:null\n"])</script><script>self.__next_f.push([1,"e:I[5552,[\"972\",\"static/chunks/972-f3553dcbba031b91.js\",\"135\",\"static/chunks/135-8f28b0a70a96c388.js\",\"28\",\"static/chunks/28-313384c6289a00c0.js\",\"369\",\"static/chunks/369-a583bbb81b96a7d8.js\",\"212\",\"static/chunks/212-67e821188dcd5fb8.js\",\"60\",\"static/chunks/60-3e1824554a9cf3f8.js\",\"203\",\"static/chunks/app/%5Blocale%5D/layout-8706067c5d993875.js\"],\"Providers\"]\nf:I[5211,[\"972\",\"static/chunks/972-f3553dcbba031b91.js\",\"135\",\"static/chunks/135-8f28b0a70a96c388.js\",\"28\",\"static/chunks/28-313384c6289a00c0.js\",\"369\",\"static/chunks/369-a583bbb81b96a7d8.js\",\"212\",\"static/chunks/212-67e821188dcd5fb8.js\",\"60\",\"static/chunks/60-3e1824554a9cf3f8.js\",\"203\",\"static/chunks/app/%5Blocale%5D/layout-8706067c5d993875.js\"],\"Layout\"]\n"])</script><script>self.__next_f.push([1,"a:[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"h-full\",\"suppressHydrationWarning\":true,\"children\":[\"$\",\"body\",null,{\"className\":\"flex min-h-full bg-white antialiased dark:bg-zinc-900\",\"children\":[\"$\",\"$Le\",null,{\"children\":[\"$\",\"div\",null,{\"className\":\"w-full\",\"children\":[\"$\",\"$Lf\",null,{\"allSections\":{\"/en.mdx\":[],\"/ja.mdx\":[],\"/pt.mdx\":[],\"/explorer\":[],\"/sdks\":[{\"title\":\"Official libraries\",\"id\":\"official-libraries\"},{\"title\":\"Community libraries\",\"id\":\"community-libraries\"}],\"/articles/why-atproto\":[],\"/articles/atproto-for-distsys-engineers/en.mdx\":[{\"title\":\"Scaling the traditional Web backend\",\"id\":\"scaling-the-traditional-web-backend\"},{\"title\":\"Decentralizing our high-scale backend\",\"id\":\"decentralizing-our-high-scale-backend\"},{\"title\":\"Unifying the data model\",\"id\":\"unifying-the-data-model\"},{\"title\":\"Charting the flow of data\",\"id\":\"charting-the-flow-of-data\"},{\"title\":\"Building practical open systems\",\"id\":\"building-practical-open-systems\"}],\"/articles/atproto-for-distsys-engineers/ja.mdx\":[{\"title\":\"従来の Web バックエンドのスケーリング\",\"id\":\"web\"},{\"title\":\"大規模バックエンドの分散化\",\"id\":\"\"},{\"title\":\"データ モデルの統合\",\"id\":\"\"},{\"title\":\"データの流れを図に表す\",\"id\":\"\"},{\"title\":\"実用的なオープン システムの構築\",\"id\":\"\"}],\"/articles/atproto-for-distsys-engineers/pt.mdx\":[{\"title\":\"Escalando o backend tradicional da Web\",\"id\":\"escalando-o-backend-tradicional-da-web\"},{\"title\":\"Descentralizando nosso backend de alta escala\",\"id\":\"descentralizando-nosso-backend-de-alta-escala\"},{\"title\":\"Unificando o modelo de dados\",\"id\":\"unificando-o-modelo-de-dados\"},{\"title\":\"Mapeando o fluxo de dados\",\"id\":\"mapeando-o-fluxo-de-dados\"},{\"title\":\"Construindo sistemas abertos práticos\",\"id\":\"construindo-sistemas-abertos-praticos\"}],\"/guides/applications/en.mdx\":[{\"title\":\"Introduction\",\"id\":\"introduction\"},{\"title\":\"Step 1. Starting with our ExpressJS app\",\"id\":\"step-1-starting-with-our-express-js-app\"},{\"title\":\"Step 2. Signing in with OAuth\",\"id\":\"step-2-signing-in-with-o-auth\"},{\"title\":\"Step 3. Fetching the user's profile\",\"id\":\"step-3-fetching-the-users-profile\"},{\"title\":\"Step 4. Reading \u0026 writing records\",\"id\":\"step-4-reading-and-writing-records\"},{\"title\":\"Step 5. Creating a custom \\\"status\\\" schema\",\"id\":\"step-5-creating-a-custom-status-schema\"},{\"title\":\"Step 6. Listening to the firehose\",\"id\":\"step-6-listening-to-the-firehose\"},{\"title\":\"Step 7. Listing the latest statuses\",\"id\":\"step-7-listing-the-latest-statuses\"},{\"title\":\"Step 8. Optimistic updates\",\"id\":\"step-8-optimistic-updates\"},{\"title\":\"Thinking in AT Proto\",\"id\":\"thinking-in-at-proto\"},{\"title\":\"Next steps\",\"id\":\"next-steps\"}],\"/guides/applications/ja.mdx\":[{\"title\":\"はじめに\",\"id\":\"\"},{\"title\":\"ステップ 1. ExpressJS アプリから始める\",\"id\":\"1-express-js\"},{\"title\":\"ステップ 2. OAuth でサインイン\",\"id\":\"2-o-auth\"},{\"title\":\"ステップ 3. ユーザーのプロファイルを取得する\",\"id\":\"3\"},{\"title\":\"ステップ 4. レコードの読み取りと書き込み\",\"id\":\"4\"},{\"title\":\"ステップ 5. カスタムの「ステータス」スキーマの作成\",\"id\":\"5\"},{\"title\":\"ステップ 6. ファイアホースをリッスン\",\"id\":\"6\"},{\"title\":\"ステップ 7. 最新のステータスを一覧表示する\",\"id\":\"7\"},{\"title\":\"ステップ 8. 楽観的更新\",\"id\":\"8\"},{\"title\":\"AT Proto で考える\",\"id\":\"at-proto\"},{\"title\":\"次のステップ\",\"id\":\"\"}],\"/guides/applications/pt.mdx\":[{\"title\":\"Introdução\",\"id\":\"introducao\"},{\"title\":\"Etapa 1. Começando com nosso aplicativo ExpressJS\",\"id\":\"etapa-1-comecando-com-nosso-aplicativo-express-js\"},{\"title\":\"Etapa 2. Entrando com OAuth\",\"id\":\"etapa-2-entrando-com-o-auth\"},{\"title\":\"Etapa 3. Obtendo o perfil do usuário\",\"id\":\"etapa-3-obtendo-o-perfil-do-usuario\"},{\"title\":\"Etapa 4. Lendo e escrevendo registros\",\"id\":\"etapa-4-lendo-e-escrevendo-registros\"},{\"title\":\"Etapa 5. Criando um esquema de \\\"status\\\" personalizado\",\"id\":\"etapa-5-criando-um-esquema-de-status-personalizado\"},{\"title\":\"Etapa 6. Ouvindo o firehose\",\"id\":\"etapa-6-ouvindo-o-firehose\"},{\"title\":\"Etapa 7. Listando os status mais recentes\",\"id\":\"etapa-7-listando-os-status-mais-recentes\"},{\"title\":\"Etapa 8. Atualizações otimistas\",\"id\":\"etapa-8-atualizacoes-otimistas\"},{\"title\":\"Pensando em AT Proto\",\"id\":\"pensando-em-at-proto\"},{\"title\":\"Próximos passos\",\"id\":\"proximos-passos\"}],\"/guides/data-repos/en.mdx\":[{\"title\":\"Data Layout\",\"id\":\"data-layout\"},{\"title\":\"Identifier Types\",\"id\":\"identifier-types\"}],\"/guides/data-repos/ja.mdx\":[{\"title\":\"データ レイアウト\",\"id\":\"\"},{\"title\":\"識別子の種類\",\"id\":\"\"}],\"/guides/data-repos/pt.mdx\":[{\"title\":\"Layout de dados\",\"id\":\"layout-de-dados\"},{\"title\":\"Tipos de Identificadores\",\"id\":\"tipos-de-identificadores\"}],\"/guides/faq/en.mdx\":[{\"title\":\"Is the AT Protocol a blockchain?\",\"id\":\"is-the-at-protocol-a-blockchain\"},{\"title\":\"Why not use ActivityPub?\",\"id\":\"why-not-use-activity-pub\"},{\"title\":\"Why create Lexicon instead of using JSON-LD or RDF?\",\"id\":\"why-create-lexicon-instead-of-using-json-ld-or-rdf\"},{\"title\":\"What is “XRPC,” and why not use ___?\",\"id\":\"what-is-xrpc-and-why-not-use\"}],\"/guides/faq/ja.mdx\":[{\"title\":\"AT プロトコルはブロックチェーンですか?\",\"id\":\"at\"},{\"title\":\"ActivityPub を使用しないのはなぜですか?\",\"id\":\"activity-pub\"},{\"title\":\"JSON-LD や RDF を使用する代わりに Lexicon を作成する理由\",\"id\":\"json-ld-rdf-lexicon\"},{\"title\":\"「XRPC」とは何ですか。なぜ ___ を使用しないのですか?\",\"id\":\"xrpc\"}],\"/guides/faq/pt.mdx\":[{\"title\":\"O Protocolo AT é uma blockchain?\",\"id\":\"o-protocolo-at-e-uma-blockchain\"},{\"title\":\"Por que não usar o ActivityPub?\",\"id\":\"por-que-nao-usar-o-activity-pub\"},{\"title\":\"Por que criar o Lexicon em vez de usar JSON-LD ou RDF?\",\"id\":\"por-que-criar-o-lexicon-em-vez-de-usar-json-ld-ou-rdf\"},{\"title\":\"O que é “XRPC” e por que não usar ___?\",\"id\":\"o-que-e-xrpc-e-por-que-nao-usar\"}],\"/guides/glossary/en.mdx\":[{\"title\":\"Atmosphere\",\"id\":\"atmosphere\"},{\"title\":\"AT Protocol\",\"id\":\"at-protocol\"},{\"title\":\"PDS (Personal Data Server)\",\"id\":\"pds-personal-data-server\"},{\"title\":\"AppView\",\"id\":\"app-view\"},{\"title\":\"Relay\",\"id\":\"relay\"},{\"title\":\"Lexicon\",\"id\":\"lexicon\"},{\"title\":\"Data Repo\",\"id\":\"data-repo\"},{\"title\":\"Collection\",\"id\":\"collection\"},{\"title\":\"Record\",\"id\":\"record\"},{\"title\":\"Blob\",\"id\":\"blob\"},{\"title\":\"Label\",\"id\":\"label\"},{\"title\":\"Handle\",\"id\":\"handle\"},{\"title\":\"DID (Decentralized ID)\",\"id\":\"did-decentralized-id\"},{\"title\":\"NSID (Namespaced ID)\",\"id\":\"nsid-namespaced-id\"},{\"title\":\"TID (Timestamp ID)\",\"id\":\"tid-timestamp-id\"},{\"title\":\"CID (Content ID)\",\"id\":\"cid-content-id\"},{\"title\":\"DAG-CBOR\",\"id\":\"dag-cbor\"},{\"title\":\"XRPC\",\"id\":\"xrpc\"}],\"/guides/glossary/ja.mdx\":[{\"title\":\"Atmosphere\",\"id\":\"atmosphere\"},{\"title\":\"AT プロトコル\",\"id\":\"at\"},{\"title\":\"PDS (パーソナル データ サーバー)\",\"id\":\"pds\"},{\"title\":\"AppView\",\"id\":\"app-view\"},{\"title\":\"リレー\",\"id\":\"\"},{\"title\":\"Lexicon\",\"id\":\"lexicon\"},{\"title\":\"データ リポジトリ\",\"id\":\"\"},{\"title\":\"コレクション\",\"id\":\"\"},{\"title\":\"レコード\",\"id\":\"\"},{\"title\":\"BLOB\",\"id\":\"blob\"},{\"title\":\"ラベル\",\"id\":\"\"},{\"title\":\"ハンドル\",\"id\":\"\"},{\"title\":\"DID (分散 ID)\",\"id\":\"did-id\"},{\"title\":\"NSID (名前空間 ID)\",\"id\":\"nsid-id\"},{\"title\":\"TID (タイムスタンプ ID)\",\"id\":\"tid-id\"},{\"title\":\"CID (コンテンツ ID)\",\"id\":\"cid-id\"},{\"title\":\"DAG-CBOR\",\"id\":\"dag-cbor\"},{\"title\":\"XRPC\",\"id\":\"xrpc\"}],\"/guides/glossary/pt.mdx\":[{\"title\":\"Atmosphere\",\"id\":\"atmosphere\"},{\"title\":\"AT Protocol\",\"id\":\"at-protocol\"},{\"title\":\"PDS (Personal Data Server)\",\"id\":\"pds-personal-data-server\"},{\"title\":\"AppView\",\"id\":\"app-view\"},{\"title\":\"Relay\",\"id\":\"relay\"},{\"title\":\"Lexicon\",\"id\":\"lexicon\"},{\"title\":\"Data Repo\",\"id\":\"data-repo\"},{\"title\":\"Collection\",\"id\":\"collection\"},{\"title\":\"Record\",\"id\":\"record\"},{\"title\":\"Blob\",\"id\":\"blob\"},{\"title\":\"Label\",\"id\":\"label\"},{\"title\":\"Handle\",\"id\":\"handle\"},{\"title\":\"DID (Decentralized ID)\",\"id\":\"did-decentralized-id\"},{\"title\":\"NSID (Namespaced ID)\",\"id\":\"nsid-namespaced-id\"},{\"title\":\"TID (Timestamp ID)\",\"id\":\"tid-timestamp-id\"},{\"title\":\"CID (Content ID)\",\"id\":\"cid-content-id\"},{\"title\":\"DAG-CBOR\",\"id\":\"dag-cbor\"},{\"title\":\"XRPC\",\"id\":\"xrpc\"}],\"/guides/identity/en.mdx\":[{\"title\":\"Identifiers\",\"id\":\"identifiers\"},{\"title\":\"DID Methods\",\"id\":\"did-methods\"},{\"title\":\"Handle Resolution\",\"id\":\"handle-resolution\"}],\"/guides/identity/ja.mdx\":[{\"title\":\"識別子\",\"id\":\"\"},{\"title\":\"DID メソッド\",\"id\":\"did\"},{\"title\":\"ハンドル解決\",\"id\":\"\"}],\"/guides/identity/pt.mdx\":[{\"title\":\"Identificadores\",\"id\":\"identificadores\"},{\"title\":\"Métodos DID\",\"id\":\"metodos-did\"},{\"title\":\"Resolução de identificadores\",\"id\":\"resolucao-de-identificadores\"}],\"/guides/lexicon/en.mdx\":[{\"title\":\"Why is Lexicon needed?\",\"id\":\"why-is-lexicon-needed\"},{\"title\":\"HTTP API methods\",\"id\":\"http-api-methods\"},{\"title\":\"Record types\",\"id\":\"record-types\"},{\"title\":\"Tokens\",\"id\":\"tokens\"},{\"title\":\"Versioning\",\"id\":\"versioning\"},{\"title\":\"Schema distribution\",\"id\":\"schema-distribution\"}],\"/guides/lexicon/ja.mdx\":[{\"title\":\"Lexicon が必要な理由\",\"id\":\"lexicon\"},{\"title\":\"HTTP API メソッド\",\"id\":\"http-api\"},{\"title\":\"レコード タイプ\",\"id\":\"\"},{\"title\":\"トークン\",\"id\":\"\"},{\"title\":\"バージョン管理\",\"id\":\"\"},{\"title\":\"スキーマの配布\",\"id\":\"\"}],\"/guides/lexicon/pt.mdx\":[{\"title\":\"Por que o Lexicon é necessário?\",\"id\":\"por-que-o-lexicon-e-necessario\"},{\"title\":\"Métodos de API HTTP\",\"id\":\"metodos-de-api-http\"},{\"title\":\"Tipos de registro\",\"id\":\"tipos-de-registro\"},{\"title\":\"Tokens\",\"id\":\"tokens\"},{\"title\":\"Versionamento\",\"id\":\"versionamento\"},{\"title\":\"Distribuição de esquema\",\"id\":\"distribuicao-de-esquema\"}],\"/guides/overview/en.mdx\":[{\"title\":\"Identity\",\"id\":\"identity\"},{\"title\":\"Data repositories\",\"id\":\"data-repositories\"},{\"title\":\"Federation\",\"id\":\"federation\"},{\"title\":\"Interoperation\",\"id\":\"interoperation\"},{\"title\":\"Achieving scale\",\"id\":\"achieving-scale\"},{\"title\":\"Algorithmic choice\",\"id\":\"algorithmic-choice\"},{\"title\":\"Account portability\",\"id\":\"account-portability\"},{\"title\":\"Speech, reach, and moderation\",\"id\":\"speech-reach-and-moderation\"},{\"title\":\"Specifications\",\"id\":\"specifications\"}],\"/guides/overview/ja.mdx\":[{\"title\":\"アイデンティティ\",\"id\":\"\"},{\"title\":\"データ リポジトリ\",\"id\":\"\"},{\"title\":\"フェデレーション\",\"id\":\"\"},{\"title\":\"相互運用\",\"id\":\"\"},{\"title\":\"スケールの実現\",\"id\":\"\"},{\"title\":\"アルゴリズムの選択\",\"id\":\"\"},{\"title\":\"アカウントの移植性\",\"id\":\"\"},{\"title\":\"スピーチ、リーチ、モデレーション\",\"id\":\"\"},{\"title\":\"仕様\",\"id\":\"\"}],\"/guides/overview/pt.mdx\":[{\"title\":\"Identidade\",\"id\":\"identidade\"},{\"title\":\"Repositórios de dados\",\"id\":\"repositorios-de-dados\"},{\"title\":\"Federação\",\"id\":\"federacao\"},{\"title\":\"Interoperação\",\"id\":\"interoperacao\"},{\"title\":\"Alcançando escala\",\"id\":\"alcancando-escala\"},{\"title\":\"Escolha algorítmica\",\"id\":\"escolha-algoritmica\"},{\"title\":\"Portabilidade de conta\",\"id\":\"portabilidade-de-conta\"},{\"title\":\"Fala, alcance e moderação\",\"id\":\"fala-alcance-e-moderacao\"},{\"title\":\"Especificações\",\"id\":\"especificacoes\"}],\"/guides/self-hosting/en.mdx\":[{\"title\":\"Table of Contents\",\"id\":\"table-of-contents\"},{\"title\":\"Preparation for self-hosting PDS\",\"id\":\"preparation-for-self-hosting-pds\"},{\"title\":\"Open your cloud firewall for HTTP and HTTPS\",\"id\":\"open-your-cloud-firewall-for-http-and-https\"},{\"title\":\"Configure DNS for your domain\",\"id\":\"configure-dns-for-your-domain\"},{\"title\":\"Check that DNS is working as expected\",\"id\":\"check-that-dns-is-working-as-expected\"},{\"title\":\"Installer on Ubuntu 20.04/22.04 and Debian 11/12\",\"id\":\"installer-on-ubuntu-20-04-22-04-and-debian-11-12\"},{\"title\":\"Verifying that your PDS is online and accessible\",\"id\":\"verifying-that-your-pds-is-online-and-accessible\"},{\"title\":\"Creating an account using pdsadmin\",\"id\":\"creating-an-account-using-pdsadmin\"},{\"title\":\"Creating an account using an invite code\",\"id\":\"creating-an-account-using-an-invite-code\"},{\"title\":\"Using the Bluesky app with your PDS\",\"id\":\"using-the-bluesky-app-with-your-pds\"},{\"title\":\"Updating your PDS\",\"id\":\"updating-your-pds\"},{\"title\":\"Getting help\",\"id\":\"getting-help\"}],\"/guides/self-hosting/ja.mdx\":[{\"title\":\"目次\",\"id\":\"\"},{\"title\":\"セルフホスティング PDS の準備\",\"id\":\"pds\"},{\"title\":\"クラウド ファイアウォールを HTTP および HTTPS 用に開く\",\"id\":\"http-https\"},{\"title\":\"ドメインの DNS を構成する\",\"id\":\"dns\"},{\"title\":\"DNS が期待どおりに動作していることを確認します\",\"id\":\"dns-2\"},{\"title\":\"Ubuntu 20.04/22.04 および Debian 11/12 のインストーラー\",\"id\":\"ubuntu-20-04-22-04-debian-11-12\"},{\"title\":\"PDS がオンラインでアクセス可能であることを確認する\",\"id\":\"pds-2\"},{\"title\":\"pdsadmin を使用してアカウントを作成する\",\"id\":\"pdsadmin\"},{\"title\":\"招待コードを使用してアカウントを作成する\",\"id\":\"\"},{\"title\":\"PDS で Bluesky アプリを使用する\",\"id\":\"pds-bluesky\"},{\"title\":\"PDS の更新\",\"id\":\"pds-3\"},{\"title\":\"ヘルプの取得\",\"id\":\"\"}],\"/guides/self-hosting/pt.mdx\":[{\"title\":\"Índice\",\"id\":\"indice\"},{\"title\":\"Preparação para auto-hospedagem de PDS\",\"id\":\"preparacao-para-auto-hospedagem-de-pds\"},{\"title\":\"Abra seu firewall de nuvem para HTTP e HTTPS\",\"id\":\"abra-seu-firewall-de-nuvem-para-http-e-https\"},{\"title\":\"Configure o DNS para seu domínio\",\"id\":\"configure-o-dns-para-seu-dominio\"},{\"title\":\"Verifique se o DNS está funcionando conforme o esperado\",\"id\":\"verifique-se-o-dns-esta-funcionando-conforme-o-esperado\"},{\"title\":\"Instalador no Ubuntu 20.04/22.04 e Debian 11/12\",\"id\":\"instalador-no-ubuntu-20-04-22-04-e-debian-11-12\"},{\"title\":\"Verificando se seu PDS está online e acessível\",\"id\":\"verificando-se-seu-pds-esta-online-e-acessivel\"},{\"title\":\"Criando uma conta usando pdsadmin\",\"id\":\"criando-uma-conta-usando-pdsadmin\"},{\"title\":\"Criando uma conta usando um código de convite\",\"id\":\"criando-uma-conta-usando-um-codigo-de-convite\"},{\"title\":\"Usando o aplicativo Bluesky com seu PDS\",\"id\":\"usando-o-aplicativo-bluesky-com-seu-pds\"},{\"title\":\"Atualizando seu PDS\",\"id\":\"atualizando-seu-pds\"},{\"title\":\"Obtendo ajuda\",\"id\":\"obtendo-ajuda\"}],\"/specs/at-uri-scheme\":[],\"/specs/atp\":[{\"title\":\"Protocol Structure\",\"id\":\"protocol-structure\"},{\"title\":\"Protocol Extension and Applications\",\"id\":\"protocol-extension-and-applications\"},{\"title\":\"What Is Missing?\",\"id\":\"what-is-missing\"},{\"title\":\"Future Work\",\"id\":\"future-work\"}],\"/specs/blob\":[{\"title\":\"Blob Metadata\",\"id\":\"blob-metadata\"},{\"title\":\"Blob Lifecycle\",\"id\":\"blob-lifecycle\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/cryptography\":[{\"title\":\"ECDSA Signature Malleability\",\"id\":\"ecdsa-signature-malleability\"},{\"title\":\"Public Key Encoding\",\"id\":\"public-key-encoding\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/data-model\":[{\"title\":\"Relationship With IPLD\",\"id\":\"relationship-with-ipld\"},{\"title\":\"Data Types\",\"id\":\"data-types\"},{\"title\":\"blob Type\",\"id\":\"blob-type\"},{\"title\":\"JSON Representation\",\"id\":\"json-representation\"},{\"title\":\"Link and CID Formats\",\"id\":\"link-and-cid-formats\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security and Privacy Considerations\",\"id\":\"security-and-privacy-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/did\":[{\"title\":\"Blessed DID Methods\",\"id\":\"blessed-did-methods\"},{\"title\":\"AT Protocol DID Identifier Syntax\",\"id\":\"at-protocol-did-identifier-syntax\"},{\"title\":\"DID Documents\",\"id\":\"did-documents\"},{\"title\":\"Representation of Public Keys\",\"id\":\"representation-of-public-keys\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/event-stream\":[{\"title\":\"Streaming Wire Protocol (v0)\",\"id\":\"streaming-wire-protocol-v0\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security and Privacy Considerations\",\"id\":\"security-and-privacy-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/handle\":[{\"title\":\"Handle Identifier Syntax\",\"id\":\"handle-identifier-syntax\"},{\"title\":\"Additional Non-Syntax Restrictions\",\"id\":\"additional-non-syntax-restrictions\"},{\"title\":\"Identifier Examples\",\"id\":\"identifier-examples\"},{\"title\":\"Handle Resolution\",\"id\":\"handle-resolution\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/label\":[{\"title\":\"Schema and Data Model\",\"id\":\"schema-and-data-model\"},{\"title\":\"Value\",\"id\":\"value\"},{\"title\":\"Label Lifecycle: Negation and Expiration\",\"id\":\"label-lifecycle-negation-and-expiration\"},{\"title\":\"Signatures\",\"id\":\"signatures\"},{\"title\":\"Self-Labels in Records\",\"id\":\"self-labels-in-records\"},{\"title\":\"Labeler Service Identity\",\"id\":\"labeler-service-identity\"},{\"title\":\"Label Distribution Endpoints\",\"id\":\"label-distribution-endpoints\"},{\"title\":\"Labeler HTTP Headers\",\"id\":\"labeler-http-headers\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/lexicon\":[{\"title\":\"Overview of Types\",\"id\":\"overview-of-types\"},{\"title\":\"Lexicon Files\",\"id\":\"lexicon-files\"},{\"title\":\"Primary Type Definitions\",\"id\":\"primary-type-definitions\"},{\"title\":\"Field Type Definitions\",\"id\":\"field-type-definitions\"},{\"title\":\"String Formats\",\"id\":\"string-formats\"},{\"title\":\"When to use $type\",\"id\":\"when-to-use-type\"},{\"title\":\"Lexicon Evolution\",\"id\":\"lexicon-evolution\"},{\"title\":\"Authority and Control\",\"id\":\"authority-and-control\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/nsid\":[],\"/specs/oauth\":[{\"title\":\"Clients\",\"id\":\"clients\"},{\"title\":\"Identity Authentication\",\"id\":\"identity-authentication\"},{\"title\":\"Authorization Scopes\",\"id\":\"authorization-scopes\"},{\"title\":\"Authorization Requests\",\"id\":\"authorization-requests\"},{\"title\":\"Tokens and Session Lifetime\",\"id\":\"tokens-and-session-lifetime\"},{\"title\":\"Demonstrating Proof of Possession (DPoP)\",\"id\":\"demonstrating-proof-of-possession-d-po-p\"},{\"title\":\"Authorization Servers\",\"id\":\"authorization-servers\"},{\"title\":\"Summary of Authorization Flow\",\"id\":\"summary-of-authorization-flow\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/record-key\":[],\"/specs/repository\":[{\"title\":\"Repo Data Structure (v3)\",\"id\":\"repo-data-structure-v3\"},{\"title\":\"CAR File Serialization\",\"id\":\"car-file-serialization\"},{\"title\":\"Repository Diffs\",\"id\":\"repository-diffs\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/xrpc\":[{\"title\":\"Lexicon HTTP Endpoints\",\"id\":\"lexicon-http-endpoints\"},{\"title\":\"Authentication\",\"id\":\"authentication\"},{\"title\":\"Service Proxying\",\"id\":\"service-proxying\"},{\"title\":\"Summary of HTTP Headers\",\"id\":\"summary-of-http-headers\"},{\"title\":\"Summary of HTTP Status Codes\",\"id\":\"summary-of-http-status-codes\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security and Privacy Considerations\",\"id\":\"security-and-privacy-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}]},\"children\":[\"$\",\"$L7\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"$8\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"div\",null,{\"className\":\"absolute inset-0 -z-10 mx-0 max-w-none overflow-hidden\",\"children\":[\"$\",\"div\",null,{\"className\":\"absolute left-1/2 top-0 ml-[-38rem] h-[25rem] w-[81.25rem] dark:[mask-image:linear-gradient(white,transparent)]\",\"children\":[\"$\",\"div\",null,{\"className\":\"absolute inset-0 bg-gradient-to-r from-[#6fa5ff] to-[#9dc0fb] opacity-40 [mask-image:radial-gradient(farthest-side_at_top,white,transparent)] dark:from-[#0090f2]/30 dark:to-[#7588ff]/30 dark:opacity-100\",\"children\":[\"$\",\"svg\",null,{\"aria-hidden\":\"true\",\"className\":\"absolute inset-x-0 inset-y-[-50%] h-[200%] w-full fill-black/40 stroke-black/50 mix-blend-overlay dark:fill-white/2.5 dark:stroke-white/5\",\"children\":[[\"$\",\"defs\",null,{\"children\":[\"$\",\"pattern\",null,{\"id\":\":S1:\",\"width\":72,\"height\":56,\"patternUnits\":\"userSpaceOnUse\",\"x\":-12,\"y\":4,\"children\":[\"$\",\"path\",null,{\"d\":\"M.5 56V.5H72\",\"fill\":\"none\"}]}]}],[\"$\",\"rect\",null,{\"width\":\"100%\",\"height\":\"100%\",\"strokeWidth\":0,\"fill\":\"url(#:S1:)\"}],[\"$\",\"svg\",null,{\"x\":-12,\"y\":4,\"className\":\"overflow-visible\",\"children\":[]}]]}]}]}]}],[\"$\",\"div\",null,{\"className\":\"mx-auto flex h-full max-w-xl flex-col items-center justify-center py-16 text-center\",\"children\":[[\"$\",\"p\",null,{\"className\":\"text-sm font-semibold text-zinc-900 dark:text-white\",\"children\":\"404\"}],[\"$\",\"h1\",null,{\"className\":\"mt-2 text-2xl font-bold text-zinc-900 dark:text-white\",\"children\":\"Page not found\"}],[\"$\",\"p\",null,{\"className\":\"mt-2 text-base text-zinc-600 dark:text-zinc-400\",\"children\":\"Sorry, we couldn’t find the page you’re looking for.\"}],[\"$\",\"$L4\",null,{\"className\":\"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-blue-400/10 dark:text-blue-400 dark:ring-1 dark:ring-inset dark:ring-blue-400/20 dark:hover:bg-blue-400/10 dark:hover:text-blue-300 dark:hover:ring-blue-300 mt-8\",\"href\":\"/\",\"children\":[false,\"Back to docs\",[\"$\",\"svg\",null,{\"viewBox\":\"0 0 20 20\",\"fill\":\"none\",\"aria-hidden\":\"true\",\"className\":\"mt-0.5 h-5 w-5 -mr-1\",\"children\":[\"$\",\"path\",null,{\"stroke\":\"currentColor\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"d\":\"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9\"}]}]]}]]}]],\"notFoundStyles\":[]}]}]}]}]}]}]\n"])</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10