fix ci
diff --git a/.github/instructions/SHAHKAR.html b/.github/instructions/SHAHKAR.html new file mode 100644 index 0000000..9ece3df --- /dev/null +++ b/.github/instructions/SHAHKAR.html @@ -0,0 +1,646 @@ +<!DOCTYPE html> +<html> +<head> +<title>SHAHKAR.md</title> +<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + +<style> +/* https://github.com/microsoft/vscode/blob/master/extensions/markdown-language-features/media/markdown.css */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +body { + font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif); + font-size: var(--vscode-markdown-font-size, 14px); + padding: 0 26px; + line-height: var(--vscode-markdown-line-height, 22px); + word-wrap: break-word; +} + +#code-csp-warning { + position: fixed; + top: 0; + right: 0; + color: white; + margin: 16px; + text-align: center; + font-size: 12px; + font-family: sans-serif; + background-color:#444444; + cursor: pointer; + padding: 6px; + box-shadow: 1px 1px 1px rgba(0,0,0,.25); +} + +#code-csp-warning:hover { + text-decoration: none; + background-color:#007acc; + box-shadow: 2px 2px 2px rgba(0,0,0,.25); +} + +body.scrollBeyondLastLine { + margin-bottom: calc(100vh - 22px); +} + +body.showEditorSelection .code-line { + position: relative; +} + +body.showEditorSelection .code-active-line:before, +body.showEditorSelection .code-line:hover:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: -12px; + height: 100%; +} + +body.showEditorSelection li.code-active-line:before, +body.showEditorSelection li.code-line:hover:before { + left: -30px; +} + +.vscode-light.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(0, 0, 0, 0.15); +} + +.vscode-light.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(0, 0, 0, 0.40); +} + +.vscode-light.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +.vscode-dark.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(255, 255, 255, 0.4); +} + +.vscode-dark.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(255, 255, 255, 0.60); +} + +.vscode-dark.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +.vscode-high-contrast.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(255, 160, 0, 0.7); +} + +.vscode-high-contrast.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(255, 160, 0, 1); +} + +.vscode-high-contrast.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +img { + max-width: 100%; + max-height: 100%; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +a:focus, +input:focus, +select:focus, +textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +hr { + border: 0; + height: 2px; + border-bottom: 2px solid; +} + +h1 { + padding-bottom: 0.3em; + line-height: 1.2; + border-bottom-width: 1px; + border-bottom-style: solid; +} + +h1, h2, h3 { + font-weight: normal; +} + +table { + border-collapse: collapse; +} + +table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +table > thead > tr > th, +table > thead > tr > td, +table > tbody > tr > th, +table > tbody > tr > td { + padding: 5px 10px; +} + +table > tbody > tr + tr > td { + border-top: 1px solid; +} + +blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left-width: 5px; + border-left-style: solid; +} + +code { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 1em; + line-height: 1.357em; +} + +body.wordWrap pre { + white-space: pre-wrap; +} + +pre:not(.hljs), +pre.hljs code > div { + padding: 16px; + border-radius: 3px; + overflow: auto; +} + +pre code { + color: var(--vscode-editor-foreground); + tab-size: 4; +} + +/** Theming */ + +.vscode-light pre { + background-color: rgba(220, 220, 220, 0.4); +} + +.vscode-dark pre { + background-color: rgba(10, 10, 10, 0.4); +} + +.vscode-high-contrast pre { + background-color: rgb(0, 0, 0); +} + +.vscode-high-contrast h1 { + border-color: rgb(0, 0, 0); +} + +.vscode-light table > thead > tr > th { + border-color: rgba(0, 0, 0, 0.69); +} + +.vscode-dark table > thead > tr > th { + border-color: rgba(255, 255, 255, 0.69); +} + +.vscode-light h1, +.vscode-light hr, +.vscode-light table > tbody > tr + tr > td { + border-color: rgba(0, 0, 0, 0.18); +} + +.vscode-dark h1, +.vscode-dark hr, +.vscode-dark table > tbody > tr + tr > td { + border-color: rgba(255, 255, 255, 0.18); +} + +</style> + +<style> +/* Tomorrow Theme */ +/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ + +/* Tomorrow Comment */ +.hljs-comment, +.hljs-quote { + color: #8e908c; +} + +/* Tomorrow Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-tag, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class, +.hljs-regexp, +.hljs-deletion { + color: #c82829; +} + +/* Tomorrow Orange */ +.hljs-number, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params, +.hljs-meta, +.hljs-link { + color: #f5871f; +} + +/* Tomorrow Yellow */ +.hljs-attribute { + color: #eab700; +} + +/* Tomorrow Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet, +.hljs-addition { + color: #718c00; +} + +/* Tomorrow Blue */ +.hljs-title, +.hljs-section { + color: #4271ae; +} + +/* Tomorrow Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #8959a8; +} + +.hljs { + display: block; + overflow-x: auto; + color: #4d4d4c; + padding: 0.5em; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} +</style> + +<style> +/* + * Markdown PDF CSS + */ + + body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "Meiryo"; + padding: 0 12px; +} + +pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + border-radius: 3px; + overflow-x: auto; + white-space: pre-wrap; + overflow-wrap: break-word; +} + +pre:not(.hljs) { + padding: 23px; + line-height: 19px; +} + +blockquote { + background: rgba(127, 127, 127, 0.1); + border-color: rgba(0, 122, 204, 0.5); +} + +.emoji { + height: 1.4em; +} + +code { + font-size: 14px; + line-height: 19px; +} + +/* for inline code */ +:not(pre):not(.hljs) > code { + color: #C9AE75; /* Change the old color so it seems less like an error */ + font-size: inherit; +} + +/* Page Break : use <div class="page"/> to insert page break +-------------------------------------------------------- */ +.page { + page-break-after: always; +} + +</style> +<link rel="stylesheet" href="file:///root/projects/test-oauth/.github/instructions/assets/markdown-pdf.css" type="text/css"> +<script src="https://cdn.jsdelivr.net/npm/mermaid@11.6.0/dist/mermaid.min.js"></script> +</head> +<body> + <script> + mermaid.initialize({ + startOnLoad: true, + theme: document.body.classList.contains('vscode-dark') || document.body.classList.contains('vscode-high-contrast') + ? 'dark' + : 'default' + }); + </script> +<p><img src="https://git.netall.live/shared/test-oauth/-/raw/5cac2522b654f6387b806ecd15829e84b5626ae0/.github/instructions/assets/Final-Logo_png_e.png" alt="alt text" title="logo"></p> +<ul> +<li>Version: 1.0</li> +<li>Release: 2025-10-13</li> +<li>Author: Bahare Zargarbashi <a href="mailto:bahare.zargarbashi2@gmail.com">(E-Mail)</a></li> +</ul> +<h1 id="shahkar-api-integration-guide-for-third-parties">Shahkar API Integration Guide for Third Parties</h1> +<ul> +<li>This document provides instructions for third-party developers to integrate with Shahkar National ID verification API. The API allows you to verify if a National ID (NID) and phone number match according to Shahkar service.</li> +</ul> +<p>Note:</p> +<ul> +<li>Verification: this file is a static document. Integrators should verify all examples and endpoints against the running API in staging before production integration.</li> +</ul> +<p>Purpose and audience</p> +<ul> +<li>Audience: product owners, backend engineers, frontend engineers, security reviewers, QA.</li> +<li>Purpose: define the authentication flow, public HTTP endpoints, request/response contracts, error modes, and recommended integration patterns.</li> +</ul> +<p>Summary of topics</p> +<ul> +<li>Document authentication flow and credential management</li> +<li>Document Shahkar verification API interface and contracts</li> +<li>Provide data format specifications for NID and phone numbers</li> +<li>Include error handling and retry guidance</li> +<li>Provide token management and refresh strategies</li> +<li>Include security best practices and example usage</li> +</ul> +<p>Conventions in this document</p> +<ul> +<li>"IAM" denotes the Identity and Access Management service for authentication.</li> +<li>All endpoints use HTTPS and expect JSON bodies unless noted otherwise.</li> +</ul> +<pre><code class="language-mermaid"><div class="mermaid">flowchart LR + subgraph IAM[Identity & Access Management] + AppLogin["Application Login<br/>(Client Credentials)"] + TokenRefresh["Token Refresh"] + end + + subgraph ShahkarAPI[Shahkar Service] + Verify["Shahkar Verification<br/>(NID + Phone)"] + end + + subgraph ThirdParty[Third-Party Application] + Client["Client Application"] + end + + Client -->|POST credentials| AppLogin + AppLogin -->|access_token| Client + Client -->|POST NID+phone + Bearer token| Verify + Verify -->|true/false| Client + Client -->|token expired| TokenRefresh + TokenRefresh -->|new access_token| Client +</div></code></pre> +<p>Architecture (logical)</p> +<h2 id="service-interface-summary-public-endpoints">Service interface summary (public endpoints)</h2> +<p>Use these base endpoints (replace example host with production host):</p> +<ul> +<li>Application login endpoint: <code>POST https://auth.holoo.co.ir/api/iam/auth/login/application</code></li> +<li>Token refresh endpoint: <code>POST https://auth.holoo.co.ir/api/iam/auth/token/refresh</code></li> +<li>Shahkar verification endpoint: <code>POST https://club.holoo.co.ir/api/persona/shahkar</code></li> +</ul> +<p>All endpoints use HTTPS and expect JSON bodies unless noted otherwise.</p> +<h2 id="authentication-flow">Authentication Flow</h2> +<h3 id="step-1-obtain-application-credentials">Step 1: Obtain Application Credentials</h3> +<p>Before accessing the Shahkar API, you must be provided with:</p> +<ul> +<li><code>client_id</code>: Your application's unique identifier</li> +<li><code>client_secret</code>: Your application's secret key</li> +<li>Base URL of our IAM service (https://auth.holoo.co.ir)</li> +</ul> +<h3 id="step-2-get-access-token">Step 2: Get Access Token</h3> +<p>Use the application login endpoint to obtain an access token.</p> +<p>Application login endpoint</p> +<ul> +<li>Method: <code>POST</code></li> +<li>Endpoint: <code>https://auth.holoo.co.ir/api/iam/auth/login/application</code></li> +<li>Content-Type: <code>application/x-www-form-urlencoded</code> (optional)</li> +</ul> +<p>Headers:</p> +<ul> +<li><code>accept: application/json</code> (optional)</li> +<li><code>accept-language: en</code> (optional, defaults to "en")</li> +<li><code>Content-Type: application/x-www-form-urlencoded</code> (optional)</li> +</ul> +<p>Request parameters (form-encoded):</p> +<ul> +<li><code>client_id</code>: YOUR_CLIENT_ID</li> +<li><code>client_secret</code>: YOUR_CLIENT_SECRET</li> +</ul> +<p>Request example (curl):</p> +<pre class="hljs"><code><div>curl -X POST <span class="hljs-string">"https://auth.holoo.co.ir/api/iam/auth/login/application"</span> \ + -H <span class="hljs-string">'accept: application/json'</span> \ + -H <span class="hljs-string">'accept-language: en'</span> \ + -H <span class="hljs-string">'Content-Type: application/x-www-form-urlencoded'</span> \ + -d <span class="hljs-string">'client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET'</span> +</div></code></pre> +<p>Successful response (200) — example JSON:</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"access_token"</span>: <span class="hljs-string">"eyJhbGciOiJSUzI1NiIsImtpZCI6ImNsdWItaG9sb29fY2VydCIsInR5cCI6IkpXVCJ9…"</span>, + <span class="hljs-attr">"id_token"</span>: <span class="hljs-string">"eyJhbGciOiJSUzI1NiIsImtpZCI6ImNsdWItaG9sb29fY2VydCIsInR5cCI6IkpXVCJ9…"</span>, + <span class="hljs-attr">"refresh_token"</span>: <span class="hljs-string">"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"</span>, + <span class="hljs-attr">"token_type"</span>: <span class="hljs-string">"Bearer"</span>, + <span class="hljs-attr">"expires_in"</span>: <span class="hljs-number">180</span>, + <span class="hljs-attr">"scope"</span>: <span class="hljs-string">""</span> +} +</div></code></pre> +<p>Note: The <code>expires_in</code> value is in seconds. In this example, the token expires in 180 seconds (3 minutes).</p> +<h3 id="step-3-use-shahkar-api">Step 3: Use Shahkar API</h3> +<p>With the access token obtained, you can now call the Shahkar verification API.</p> +<p>Shahkar verification endpoint</p> +<ul> +<li>Method: <code>POST</code></li> +<li>Endpoint: <code>https://club.holoo.co.ir/api/persona/shahkar</code></li> +<li>Content-Type: <code>application/json</code></li> +</ul> +<p>Headers:</p> +<ul> +<li><code>accept: application/json</code></li> +<li><code>Authorization: Bearer YOUR_ACCESS_TOKEN</code></li> +<li><code>Content-Type: application/json</code></li> +</ul> +<p>Request body (JSON):</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"nid"</span>: <span class="hljs-string">"1234567890"</span>, + <span class="hljs-attr">"phone"</span>: <span class="hljs-string">"+989123456789"</span> +} +</div></code></pre> +<p>Request example (curl):</p> +<pre class="hljs"><code><div>curl -X POST <span class="hljs-string">"https://club.holoo.co.ir/api/persona/shahkar"</span> \ + -H <span class="hljs-string">'accept: application/json'</span> \ + -H <span class="hljs-string">'Authorization: Bearer YOUR_ACCESS_TOKEN'</span> \ + -H <span class="hljs-string">'Content-Type: application/json'</span> \ + -d <span class="hljs-string">'{ + "nid": "1234567890", + "phone": "+989123456789" + }'</span> +</div></code></pre> +<p>Successful response (200):</p> +<pre class="hljs"><code><div><span class="hljs-literal">true</span> +</div></code></pre> +<p>or</p> +<pre class="hljs"><code><div><span class="hljs-literal">false</span> +</div></code></pre> +<pre><code class="language-mermaid"><div class="mermaid">sequenceDiagram + participant Client as Third-Party Client + participant IAM as IAM Service + participant Shahkar as Shahkar API + + Client->>IAM: POST /login/application<br/>(client_id, client_secret) + IAM->>Client: 200 - access_token, refresh_token, expires_in + Client->>Shahkar: POST /shahkar<br/>(nid, phone, Bearer token) + Shahkar->>Client: 200 - true or false + Note over Client: Token expires after 180s + Client->>IAM: POST /token/refresh<br/>(refresh_token) + IAM->>Client: 200 - new access_token, new refresh_token +</div></code></pre> +<p>Shahkar Verification Flow</p> +<h2 id="data-formats">Data Formats</h2> +<h3 id="national-id-nid">National ID (NID)</h3> +<ul> +<li>Type: String</li> +<li>Format: 10-digit Iranian national identification number</li> +<li>Example: <code>"1234567890"</code></li> +</ul> +<h3 id="phone-number">Phone Number</h3> +<ul> +<li>Type: String</li> +<li>Format: International format with country code</li> +<li>Examples: +<ul> +<li><code>"+989123456789"</code> (Iranian mobile with +)</li> +</ul> +</li> +<li>Validation: Must be a valid phone number format</li> +</ul> +<h2 id="error-handling">Error Handling</h2> +<h3 id="authentication-errors">Authentication Errors</h3> +<p>401 Unauthorized — Invalid credentials:</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"detail"</span>: <span class="hljs-string">"Invalid credentials"</span> +} +</div></code></pre> +<p>403 Forbidden — Token expired or invalid:</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"detail"</span>: <span class="hljs-string">"Token expired or invalid"</span> +} +</div></code></pre> +<p>422 Unprocessable Entity — Invalid Phone or NID:</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"detail"</span>: <span class="hljs-string">"Invalid Phone or NID"</span> +} +</div></code></pre> +<h3 id="service-errors">Service Errors</h3> +<p>503 Service Unavailable — Connection timeout:</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"detail"</span>: <span class="hljs-string">"Shahkar verification failed: Connection timeout"</span> +} +</div></code></pre> +<p>Note: Implement retry logic with exponential backoff for 503 errors.</p> +<h2 id="token-management">Token Management</h2> +<h3 id="token-expiration">Token Expiration</h3> +<ul> +<li>Access tokens expire after 180 seconds (3 minutes) by default</li> +<li>Monitor token expiry and refresh proactively before making API calls</li> +</ul> +<h3 id="token-refresh">Token Refresh</h3> +<p>If your access token expires, you can either:</p> +<ol> +<li>Re-authenticate using the application login endpoint (recommended for server-to-server):</li> +</ol> +<ul> +<li>Simply call the login endpoint again with your credentials</li> +<li>This is the simplest approach for machine-to-machine authentication</li> +</ul> +<ol start="2"> +<li>Use refresh token:</li> +</ol> +<ul> +<li>URL: <code>POST https://auth.holoo.co.ir/api/iam/auth/token/refresh</code></li> +<li>Method: <code>POST</code> with content-type <code>application/json</code></li> +</ul> +<p>Request body (JSON):</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"token"</span>: <span class="hljs-string">"YOUR_REFRESH_TOKEN"</span> +} +</div></code></pre> +<p>Request example (curl):</p> +<pre class="hljs"><code><div>curl -X POST <span class="hljs-string">"https://auth.holoo.co.ir/api/iam/auth/token/refresh"</span> \ + -H <span class="hljs-string">'Content-Type: application/json'</span> \ + -d <span class="hljs-string">'{ + "token": "YOUR_REFRESH_TOKEN" + }'</span> +</div></code></pre> +<p>Successful response (200) — example JSON:</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"access_token"</span>: <span class="hljs-string">"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"</span>, + <span class="hljs-attr">"id_token"</span>: <span class="hljs-string">"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"</span>, + <span class="hljs-attr">"refresh_token"</span>: <span class="hljs-string">"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"</span>, + <span class="hljs-attr">"token_type"</span>: <span class="hljs-string">"Bearer"</span>, + <span class="hljs-attr">"expires_in"</span>: <span class="hljs-number">180</span>, + <span class="hljs-attr">"scope"</span>: <span class="hljs-string">""</span> +} +</div></code></pre> +<h2 id="appendix--example-requests-curl">Appendix — Example Requests (curl)</h2> +<p>Complete flow example:</p> +<pre class="hljs"><code><div><span class="hljs-comment"># 1) Authenticate and obtain access token</span> +curl -X POST <span class="hljs-string">"https://auth.holoo.co.ir/api/iam/auth/login/application"</span> \ + -H <span class="hljs-string">'accept: application/json'</span> \ + -H <span class="hljs-string">'accept-language: en'</span> \ + -H <span class="hljs-string">'Content-Type: application/x-www-form-urlencoded'</span> \ + -d <span class="hljs-string">'client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET'</span> + +<span class="hljs-comment"># Response: { "access_token": "...", "expires_in": 180, ... }</span> + +<span class="hljs-comment"># 2) Use access token to verify NID and phone</span> +curl -X POST <span class="hljs-string">"https://club.holoo.co.ir/api/persona/shahkar"</span> \ + -H <span class="hljs-string">'accept: application/json'</span> \ + -H <span class="hljs-string">'Authorization: Bearer YOUR_ACCESS_TOKEN'</span> \ + -H <span class="hljs-string">'Content-Type: application/json'</span> \ + -d <span class="hljs-string">'{ + "nid": "1234567890", + "phone": "+989123456789" + }'</span> + +<span class="hljs-comment"># Response: true or false</span> + +<span class="hljs-comment"># 3) Refresh token when expired</span> +curl -X POST <span class="hljs-string">"https://auth.holoo.co.ir/api/iam/auth/token/refresh"</span> \ + -H <span class="hljs-string">'Content-Type: application/json'</span> \ + -d <span class="hljs-string">'{ + "token": "YOUR_REFRESH_TOKEN" + }'</span> + +<span class="hljs-comment"># Response: { "access_token": "...", "refresh_token": "...", ... }</span> +</div></code></pre> + +</body> +</html> diff --git a/.github/instructions/SSO_CATALOG.instructions.html b/.github/instructions/SSO_CATALOG.instructions.html new file mode 100644 index 0000000..6bcb7d2 --- /dev/null +++ b/.github/instructions/SSO_CATALOG.instructions.html @@ -0,0 +1,669 @@ +<!DOCTYPE html> +<html> +<head> +<title>SSO_CATALOG.instructions.md</title> +<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + +<style> +/* https://github.com/microsoft/vscode/blob/master/extensions/markdown-language-features/media/markdown.css */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +body { + font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif); + font-size: var(--vscode-markdown-font-size, 14px); + padding: 0 26px; + line-height: var(--vscode-markdown-line-height, 22px); + word-wrap: break-word; +} + +#code-csp-warning { + position: fixed; + top: 0; + right: 0; + color: white; + margin: 16px; + text-align: center; + font-size: 12px; + font-family: sans-serif; + background-color:#444444; + cursor: pointer; + padding: 6px; + box-shadow: 1px 1px 1px rgba(0,0,0,.25); +} + +#code-csp-warning:hover { + text-decoration: none; + background-color:#007acc; + box-shadow: 2px 2px 2px rgba(0,0,0,.25); +} + +body.scrollBeyondLastLine { + margin-bottom: calc(100vh - 22px); +} + +body.showEditorSelection .code-line { + position: relative; +} + +body.showEditorSelection .code-active-line:before, +body.showEditorSelection .code-line:hover:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: -12px; + height: 100%; +} + +body.showEditorSelection li.code-active-line:before, +body.showEditorSelection li.code-line:hover:before { + left: -30px; +} + +.vscode-light.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(0, 0, 0, 0.15); +} + +.vscode-light.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(0, 0, 0, 0.40); +} + +.vscode-light.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +.vscode-dark.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(255, 255, 255, 0.4); +} + +.vscode-dark.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(255, 255, 255, 0.60); +} + +.vscode-dark.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +.vscode-high-contrast.showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(255, 160, 0, 0.7); +} + +.vscode-high-contrast.showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(255, 160, 0, 1); +} + +.vscode-high-contrast.showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +img { + max-width: 100%; + max-height: 100%; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +a:focus, +input:focus, +select:focus, +textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +hr { + border: 0; + height: 2px; + border-bottom: 2px solid; +} + +h1 { + padding-bottom: 0.3em; + line-height: 1.2; + border-bottom-width: 1px; + border-bottom-style: solid; +} + +h1, h2, h3 { + font-weight: normal; +} + +table { + border-collapse: collapse; +} + +table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +table > thead > tr > th, +table > thead > tr > td, +table > tbody > tr > th, +table > tbody > tr > td { + padding: 5px 10px; +} + +table > tbody > tr + tr > td { + border-top: 1px solid; +} + +blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left-width: 5px; + border-left-style: solid; +} + +code { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 1em; + line-height: 1.357em; +} + +body.wordWrap pre { + white-space: pre-wrap; +} + +pre:not(.hljs), +pre.hljs code > div { + padding: 16px; + border-radius: 3px; + overflow: auto; +} + +pre code { + color: var(--vscode-editor-foreground); + tab-size: 4; +} + +/** Theming */ + +.vscode-light pre { + background-color: rgba(220, 220, 220, 0.4); +} + +.vscode-dark pre { + background-color: rgba(10, 10, 10, 0.4); +} + +.vscode-high-contrast pre { + background-color: rgb(0, 0, 0); +} + +.vscode-high-contrast h1 { + border-color: rgb(0, 0, 0); +} + +.vscode-light table > thead > tr > th { + border-color: rgba(0, 0, 0, 0.69); +} + +.vscode-dark table > thead > tr > th { + border-color: rgba(255, 255, 255, 0.69); +} + +.vscode-light h1, +.vscode-light hr, +.vscode-light table > tbody > tr + tr > td { + border-color: rgba(0, 0, 0, 0.18); +} + +.vscode-dark h1, +.vscode-dark hr, +.vscode-dark table > tbody > tr + tr > td { + border-color: rgba(255, 255, 255, 0.18); +} + +</style> + +<style> +/* Tomorrow Theme */ +/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ + +/* Tomorrow Comment */ +.hljs-comment, +.hljs-quote { + color: #8e908c; +} + +/* Tomorrow Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-tag, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class, +.hljs-regexp, +.hljs-deletion { + color: #c82829; +} + +/* Tomorrow Orange */ +.hljs-number, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params, +.hljs-meta, +.hljs-link { + color: #f5871f; +} + +/* Tomorrow Yellow */ +.hljs-attribute { + color: #eab700; +} + +/* Tomorrow Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet, +.hljs-addition { + color: #718c00; +} + +/* Tomorrow Blue */ +.hljs-title, +.hljs-section { + color: #4271ae; +} + +/* Tomorrow Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #8959a8; +} + +.hljs { + display: block; + overflow-x: auto; + color: #4d4d4c; + padding: 0.5em; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} +</style> + +<style> +/* + * Markdown PDF CSS + */ + + body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "Meiryo"; + padding: 0 12px; +} + +pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + border-radius: 3px; + overflow-x: auto; + white-space: pre-wrap; + overflow-wrap: break-word; +} + +pre:not(.hljs) { + padding: 23px; + line-height: 19px; +} + +blockquote { + background: rgba(127, 127, 127, 0.1); + border-color: rgba(0, 122, 204, 0.5); +} + +.emoji { + height: 1.4em; +} + +code { + font-size: 14px; + line-height: 19px; +} + +/* for inline code */ +:not(pre):not(.hljs) > code { + color: #C9AE75; /* Change the old color so it seems less like an error */ + font-size: inherit; +} + +/* Page Break : use <div class="page"/> to insert page break +-------------------------------------------------------- */ +.page { + page-break-after: always; +} + +</style> +<link rel="stylesheet" href="file:///root/projects/test-oauth/.github/instructions/assets/markdown-pdf.css" type="text/css"> +<script src="https://cdn.jsdelivr.net/npm/mermaid@11.6.0/dist/mermaid.min.js"></script> +</head> +<body> + <script> + mermaid.initialize({ + startOnLoad: true, + theme: document.body.classList.contains('vscode-dark') || document.body.classList.contains('vscode-high-contrast') + ? 'dark' + : 'default' + }); + </script> +<p><img src="assets/Final-Logo_png_e.png" alt="alt text" title="logo"></p> +<ul> +<li>Version: 1.0</li> +<li>Release: 2025-09-07</li> +<li>Author: Sepehr Solouki <a href="mailto:sephr.solouki@gmail.com">(E-Mail)</a></li> +</ul> +<h1 id="sso-catalog--identity-provider-idp-interfaces-and-rp-integration-guide">SSO Catalog — Identity Provider (IdP) Interfaces and RP Integration Guide</h1> +<ul> +<li>This document is the organization's canonical SSO catalog. It describes the public interfaces, contracts, flows, and expectations for teams that want to use the SSO service as their Identity Provider (IdP) or implement a Relying Party (RP). It combines the OAuth 2.0 Authorization Code flow and the OAuth 2.0 Device Flow into a single, actionable reference.</li> +</ul> +<p>Note:</p> +<ul> +<li>Verification: this file is a static document. Integrators should verify all examples and endpoints against the running IdP in staging before production integration.</li> +</ul> +<p>Purpose and audience</p> +<ul> +<li>Audience: product owners, backend engineers, frontend engineers, desktop/mobile client teams, security reviewers, QA.</li> +<li>Purpose: define the public HTTP endpoints, request/response contracts, TTLs, error modes, and recommended integration patterns.</li> +</ul> +<p>Summary of topics</p> +<ul> +<li>Document Authorization Code flow interfaces and contracts</li> +<li>Document OAuth Device Flow (client initiation, verification, polling)</li> +<li>Provide architecture diagram showing IdP and RP components</li> +<li>Provide sequence diagrams for both flows</li> +<li>Include TTLs, error responses, and polling guidance</li> +<li>Provide integration checklist for RPs and example usage</li> +</ul> +<p>Conventions in this document</p> +<ul> +<li>Replace domains with placeholders such as <code>https://auth.holoo.co.ir</code> and <code>https://club.holoo.co.ir</code>.</li> +<li>"IdP" denotes the SSO service and its auth/token subsystems.</li> +<li>"RP" denotes any relying party (web app, backend, or native client).</li> +</ul> +<pre><code class="language-mermaid"><div class="mermaid">flowchart LR + subgraph IDP[Identity Provider] + Auth["Authorization Endpoint<br/>(Token, Consent)"] + Token["Token Endpoint"] + Introspect["Token Introspection"] + UserInfo["UserInfo Endpoint"] + DeviceStore[(Device Store)] + SLO["SLO / Session Management"] + end + + subgraph RP[Example Relying Parties] + WebFrontend["Web Frontend (Browser RP)"] + BackendRP["Backend RP"] + DeviceClient["Device Client (native)"] + end + + WebFrontend -->|browser redirect| Auth + BackendRP -->|server-to-server| Token + DeviceClient -->|device token initiate| DeviceStore + DeviceClient -->|device poll| Token + Auth --> Token + Token --> Introspect + Token --> UserInfo + SLO --> WebFrontend + SLO --> BackendRP +</div></code></pre> +<p>Architecture (logical)</p> +<h2 id="service-interface-summary-public-endpoints">Service interface summary (public endpoints)</h2> +<p>Use these base endpoints on the IdP (replace example host with production host):</p> +<ul> +<li>Authorization endpoint (browser): <code>https://auth.example.com/sso/authorize</code></li> +<li>Token exchange endpoint (server): <code>POST https://auth.example.com/api/login/oauth/access_token</code></li> +<li>Token refresh endpoint: <code>POST https://auth.example.com/api/login/oauth/refresh_token</code></li> +<li>Token introspection: <code>POST https://auth.example.com/api/login/oauth/introspect</code></li> +<li>UserInfo: <code>GET https://auth.example.com/api/userinfo</code></li> +<li>Device endpoints: +<ul> +<li>Initiate device flow: <code>POST https://auth.example.com/api/iam/device/token</code></li> +<li>Verify device code (browser): <code>POST https://auth.example.com/api/iam/device/verify</code></li> +<li>Poll for tokens (device client): <code>POST https://auth.example.com/api/iam/device/poll</code></li> +</ul> +</li> +<li>SSO logout (RP-initiated): <code>https://auth.example.com/sso/logout</code></li> +</ul> +<p>All endpoints use HTTPS and expect JSON bodies unless noted otherwise.</p> +<h2 id="oauth-20-authorization-code--contract">OAuth 2.0 Authorization Code — Contract</h2> +<p>Short contract</p> +<ul> +<li>Input: browser redirect to the authorization endpoint with <code>client_id</code>, <code>redirect_uri</code>, <code>response_type=code</code>, <code>scope</code>, and <code>state</code>.</li> +<li>Output: authorization <code>code</code> returned to <code>redirect_uri</code> as <code>?code=...&state=...</code>.</li> +<li>Next: the RP backend exchanges the authorization code for tokens at the token endpoint.</li> +</ul> +<p>Authorization endpoint</p> +<ul> +<li>Method: <code>GET</code> (browser redirect)</li> +<li>Endpoint: <code>https://auth.example.com/sso/authorize</code></li> +<li>Query parameters: <code>client_id</code>, <code>response_type=code</code>, <code>redirect_uri</code>, <code>scope</code>, <code>state</code></li> +</ul> +<p>Example browser request (user agent redirect)</p> +<pre class="hljs"><code><div>GET https://auth.example.com/sso/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=https://app.example.com/callback&scope=openid%20profile&state=RANDOM +</div></code></pre> +<p>Token exchange (server-side)</p> +<ul> +<li>URL: <code>POST https://auth.example.com/api/login/oauth/access_token</code></li> +<li>Method: <code>POST</code> with content-type <code>application/x-www-form-urlencoded</code> (recommended for confidential clients). JSON may be accepted by some implementations.</li> +</ul> +<p>Request examples (token exchange)</p> +<p>Form-encoded (recommended):</p> +<pre class="hljs"><code><div>grant_type=authorization_code&client_id=YOUR_CLIENT_ID&client_secret=YOUR_SECRET&code=AUTH_CODE&redirect_uri=https://app.example.com/callback +</div></code></pre> +<p>JSON (if supported):</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"grant_type"</span>: <span class="hljs-string">"authorization_code"</span>, + <span class="hljs-attr">"client_id"</span>: <span class="hljs-string">"YOUR_CLIENT_ID"</span>, + <span class="hljs-attr">"client_secret"</span>: <span class="hljs-string">"YOUR_SECRET"</span>, + <span class="hljs-attr">"code"</span>: <span class="hljs-string">"AUTH_CODE"</span>, + <span class="hljs-attr">"redirect_uri"</span>: <span class="hljs-string">"https://app.example.com/callback"</span> +} +</div></code></pre> +<p>Successful response (200) — example JSON</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"access_token"</span>: <span class="hljs-string">"2YotnFZFEjr1zCsicMWpAA..."</span>, + <span class="hljs-attr">"token_type"</span>: <span class="hljs-string">"Bearer"</span>, + <span class="hljs-attr">"expires_in"</span>: <span class="hljs-number">180</span>, + <span class="hljs-attr">"refresh_token"</span>: <span class="hljs-string">"tGzv3JOkF0XG5Qx2TlKWIA..."</span>, + <span class="hljs-attr">"id_token"</span>: <span class="hljs-string">"eyJ..."</span> +} +</div></code></pre> +<p>Errors</p> +<ul> +<li><code>400</code>: <code>invalid_request</code> / <code>invalid_grant</code></li> +<li><code>401</code>: <code>invalid_client</code> (when client authentication fails)</li> +</ul> +<p>Introspection</p> +<ul> +<li>URL: <code>POST https://auth.example.com/api/login/oauth/introspect</code></li> +<li>Requires client authentication (<code>HTTP Basic</code> with <code>client_id:client_secret</code>)</li> +</ul> +<p>Request example (form-encoded):</p> +<pre class="hljs"><code><div>token=THE_TOKEN&token_type_hint=access_token +</div></code></pre> +<p>Response: token metadata fields such as <code>active</code>, <code>scope</code>, <code>client_id</code>, <code>exp</code>, <code>sub</code>.</p> +<p>UserInfo</p> +<ul> +<li>URL: <code>GET https://auth.example.com/api/userinfo</code></li> +<li>Authorization header: <code>Authorization: Bearer <access_token></code></li> +<li>Response: standard OIDC claims for the authenticated user (JSON).</li> +</ul> +<p>Logout</p> +<ul> +<li>RPs should implement both local logout and SSO logout redirect.</li> +<li>For complete SSO logout, redirect the user to <code>https://auth.example.com/sso/logout?client_name=...&redirect_uri=...&state=...</code> and perform local cleanup when the user returns.</li> +</ul> +<pre><code class="language-mermaid"><div class="mermaid">sequenceDiagram + participant Browser + participant RP as "RP (web)" + participant IdP + + Browser->>RP: request protected resource + RP->>Browser: redirect to IdP /authorize + Browser->>IdP: GET /authorize (client_id, redirect_uri, state) + IdP->>Browser: login/consent UI + Browser->>IdP: user posts credentials + IdP->>Browser: redirect to redirect_uri?code=...&state=... + Browser->>RP: returns to RP callback + RP->>IdP: POST /access_token (code, client_id, client_secret) + IdP->>RP: JSON { access_token, refresh_token, expires_in } +</div></code></pre> +<p>Authorization Code</p> +<h2 id="oauth-20-device-flow--contract">OAuth 2.0 Device Flow — Contract</h2> +<p>Short contract</p> +<ul> +<li>Device client (no browser or limited input) initiates flow to obtain a <code>user_code</code> and <code>device_code</code>.</li> +<li>User opens verification URL on a separate browser and enters <code>user_code</code> to authorize the device.</li> +<li>Device polls token endpoint until tokens are issued or error conditions occur.</li> +</ul> +<ol> +<li>Initiate (device -> IdP)</li> +</ol> +<ul> +<li>URL: <code>POST https://auth.example.com/api/iam/device/token</code></li> +<li>Method: <code>POST</code> with content-type <code>application/json</code></li> +</ul> +<p>Request body (JSON):</p> +<pre class="hljs"><code><div>{ <span class="hljs-attr">"response_type"</span>: <span class="hljs-string">"device_code"</span>, <span class="hljs-attr">"client_id"</span>: <span class="hljs-string">"<client_id>"</span> } +</div></code></pre> +<p>Successful response (200) — example JSON:</p> +<pre class="hljs"><code><div>{ + <span class="hljs-attr">"verification_uri"</span>: <span class="hljs-string">"https://app.example.com/device-auth"</span>, + <span class="hljs-attr">"user_code"</span>: <span class="hljs-string">"09421"</span>, + <span class="hljs-attr">"device_code"</span>: <span class="hljs-string">"0P8u...longtoken...xY"</span>, + <span class="hljs-attr">"interval"</span>: <span class="hljs-number">5</span> +} +</div></code></pre> +<p>Notes: The server validates <code>client_id</code>; only registered clients are allowed.</p> +<ol start="2"> +<li>User verification (browser)</li> +</ol> +<ul> +<li>The user opens the <code>verification_uri</code> (e.g. https://app.example.com/device-auth) in a separate browser where they are already authenticated.</li> +<li>The user enters the <code>user_code</code> in the verification page to authorize the device.</li> +<li><strong>Note</strong>: This step is performed directly between the user's browser and the IdP - the RP is not involved in this verification step.</li> +<li>On success, the IdP associates the user's identity with the device code and extends the TTL so the device can poll and retrieve tokens.</li> +</ul> +<ol start="3"> +<li>Polling (device client)</li> +</ol> +<ul> +<li>URL: <code>POST https://auth.example.com/api/iam/device/poll</code></li> +<li>Method: <code>POST</code> with content-type <code>application/json</code></li> +</ul> +<p>Request body (JSON):</p> +<pre class="hljs"><code><div>{ <span class="hljs-attr">"grant_type"</span>: <span class="hljs-string">"device_code"</span>, <span class="hljs-attr">"client_id"</span>: <span class="hljs-string">"<client_id>"</span>, <span class="hljs-attr">"device_code"</span>: <span class="hljs-string">"<device_code>"</span> } +</div></code></pre> +<p>Possible server responses:</p> +<pre class="hljs"><code><div><span class="hljs-comment">// success (200)</span> +{ <span class="hljs-attr">"access_token"</span>: <span class="hljs-string">"..."</span>, <span class="hljs-attr">"refresh_token"</span>: <span class="hljs-string">"..."</span>, <span class="hljs-attr">"token_type"</span>: <span class="hljs-string">"Bearer"</span>, <span class="hljs-attr">"expires_in"</span>: <span class="hljs-number">180</span> } + +<span class="hljs-comment">// pending (400)</span> +{ <span class="hljs-attr">"detail"</span>: <span class="hljs-string">"authorization_pending"</span> } + +<span class="hljs-comment">// slow down (400)</span> +{ <span class="hljs-attr">"detail"</span>: <span class="hljs-string">"slow_down"</span> } + +<span class="hljs-comment">// invalid (400)</span> +{ <span class="hljs-attr">"detail"</span>: <span class="hljs-string">"invalid_grant"</span> } +</div></code></pre> +<p>Polling behaviour and TTLs</p> +<ul> +<li>The <code>interval</code> is the suggested poll interval (default 5s). Respect <code>slow_down</code> by increasing interval.</li> +<li>Initial device entry TTL: 300 seconds (5 minutes). If user verifies the code, the server replaces the payload with the token object and extends TTL to 600 seconds (10 minutes) to let the device poll and retrieve tokens.</li> +<li>When <code>device_poll</code> returns the token, the device entry is deleted (one-time use).</li> +</ul> +<pre><code class="language-mermaid"><div class="mermaid">sequenceDiagram + participant Device + participant IdP + participant Browser + + Device->>IdP: POST /device/token (client_id) + IdP->>Device: { verification_uri, user_code, device_code, interval } + Device->>User: show user_code and verification_uri + User->>Browser: open verification_uri + Browser->>IdP: POST /device/verify (user_code) [authenticated user approves] + IdP->>Device: (no direct response) — mark device as authorized + Device->>IdP: POST /device/poll (device_code) + IdP->>Device: 200 { access_token, refresh_token, expires_in } OR 400 { authorization_pending } +</div></code></pre> +<p>Device Flow</p> +<h2 id="security-considerations">Security considerations</h2> +<ul> +<li>Clients (RPs) MUST call IdP endpoints over TLS (HTTPS).</li> +<li>Treat <code>user_code</code> as short-lived, low-entropy: show it only to the human and never persist in logs.</li> +<li>Validate tokens' expiry locally via <code>exp</code> parameter of their claims and their authenticity via introspection.</li> +<li>Protect refresh tokens and store them securely (server-side for confidential RPs, secure keystore for native apps).</li> +<li>Use state and PKCE for public clients during authorization code flow to mitigate CSRF and code interception.</li> +<li>State parameters shouldn't be the same for every flow. Aside from security concerns, this behavior may lead into unintended cached responses by browsers or intermediate proxies.</li> +</ul> +<h2 id="rp-registration--runtime-checklist">RP registration & runtime checklist</h2> +<ul> +<li>Register client (client_id, client_secret for confidential clients, redirect_uris, allowed logout URIs, front/back-channel logout URIs, support flags for device flow) with IdP Admin.</li> +<li>For Authorization Code RPs (web): implement server-side code exchange and secure storage of refresh tokens.</li> +<li>For Public/native clients using Device Flow: display <code>user_code</code> clearly, open verification URI in user's browser, store refresh token in secure OS keystore.</li> +<li>For any RP: implement token introspection or token validation (JWT signature + exp check) depending on token formats used.</li> +</ul> +<h3 id="rp-registration-process">RP registration process</h3> +<ul> +<li>Applications request client credentials by submitting:</li> +</ul> +<pre class="hljs"><code><div>Application name +Description +Logo URL +Redirect URIs (one or more) +Contact owner email +</div></code></pre> +<ul> +<li>The SSO maintainers validate request, register the client, and return <code>client_id</code> (and <code>client_secret</code> for confidential apps) with permitted redirect URIs and flags.</li> +</ul> +<h2 id="error-modes-and-guidance">Error modes and guidance</h2> +<ul> +<li>invalid_client / invalid_request: client misconfiguration — check registration and request parameters.</li> +<li>invalid_grant (device_poll): treat as terminal — restart device flow.</li> +<li>authorization_pending: continue polling until expiry.</li> +<li>slow_down: back off polling interval (add +5s or multiply interval).</li> +</ul> +<h2 id="appendix--example-requests-curl">Appendix — Example requests (curl)</h2> +<p>Authorization Code flow (browser + server exchange)</p> +<pre class="hljs"><code><div><span class="hljs-comment"># 1) Browser: redirect user to IdP authorization endpoint</span> +curl -X GET <span class="hljs-string">'https://auth.example.com/sso/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=https://app.example.com/callback&scope=openid%20profile&state=RANDOM'</span> + +<span class="hljs-comment"># 2) RP backend: exchange code for tokens</span> +curl -X POST <span class="hljs-string">'https://auth.example.com/api/login/oauth/access_token'</span> \ + -H <span class="hljs-string">'Content-Type: application/x-www-form-urlencoded'</span> \ + -d <span class="hljs-string">'grant_type=authorization_code&client_id=YOUR_CLIENT_ID&client_secret=YOUR_SECRET&code=CODE_FROM_CALLBACK&redirect_uri=https://app.example.com/callback'</span> +</div></code></pre> +<p>Device Flow (device client)</p> +<pre class="hljs"><code><div><span class="hljs-comment"># 1) Initiate device flow (device)</span> +curl -X POST <span class="hljs-string">'https://auth.example.com/api/iam/device/token'</span> \ + -H <span class="hljs-string">'Content-Type: application/json'</span> \ + -d <span class="hljs-string">'{"response_type":"device_code","client_id":"YOUR_CLIENT_ID"}'</span> + +<span class="hljs-comment"># 2) Device polls for tokens</span> +curl -X POST <span class="hljs-string">'https://auth.example.com/api/iam/device/poll'</span> \ + -H <span class="hljs-string">'Content-Type: application/json'</span> \ + -d <span class="hljs-string">'{"grant_type":"device_code","client_id":"YOUR_CLIENT_ID","device_code":"DEVICE_CODE"}'</span> +</div></code></pre> + +</body> +</html>
Loading
Please register or sign in to comment