> ## Documentation Index
> Fetch the complete documentation index at: https://developers.authlete.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth 2.0 Basics

> A tutorial to understand how OAuth 2.0 authorization server leverages Authlete APIs.

export const _ReflectCell = ({id}) => {
  const [value, setValue] = useState(' ');
  const update = useCallback(() => {
    if (typeof document === 'undefined') return;
    const el = document.getElementById(id);
    setValue((el ? el.value || el.placeholder || '' : '').trim() || ' ');
  }, [id]);
  useEffect(() => {
    update();
    const el = document.getElementById(id);
    if (el) {
      el.addEventListener('input', update);
      return () => el.removeEventListener('input', update);
    }
    const iv = setInterval(update, 300);
    return () => clearInterval(iv);
  }, [update]);
  return <code className="text-zinc-800 dark:text-zinc-200">{value}</code>;
};

export const TextReflect = ({id}) => <_ReflectCell id={id} />;

export const InputTable = ({rows, col1 = 'Item', col2 = 'Value'}) => <div className="rounded-xl overflow-hidden border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-900/50 my-4 shadow-sm">
    <table className="w-full text-sm">
      <thead>
        <tr className="border-b border-zinc-200 dark:border-zinc-700 bg-zinc-100/80 dark:bg-zinc-800/50">
          <th className="text-left font-medium text-zinc-700 dark:text-zinc-300 px-4 py-3 whitespace-nowrap">{col1}</th>
          <th className="text-left font-medium text-zinc-700 dark:text-zinc-300 px-4 py-3">{col2}</th>
        </tr>
      </thead>
      <tbody className="divide-y divide-zinc-200 dark:divide-zinc-700">
        {rows.map((row, i) => <tr key={i}>
            <td className="px-4 py-3 text-zinc-600 dark:text-zinc-400 whitespace-nowrap">
              {row.code ? <><code className="text-zinc-800 dark:text-zinc-200">{row.code}</code>{row.label ? ' ' + row.label : ''}</> : row.label}
            </td>
            <td className="px-4 py-3">
              {('static' in row) ? <code className="text-zinc-800 dark:text-zinc-200">{row.static}</code> : ('reflect' in row) ? <_ReflectCell id={row.reflect} /> : <input type="text" id={row.id} defaultValue={row.defaultValue} placeholder={row.placeholder} className="w-full max-w-md px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 outline-none placeholder-zinc-400 dark:placeholder-zinc-500" />}
            </td>
          </tr>)}
      </tbody>
    </table>
  </div>;

export const DynamicCurl = ({children, inputIds = [], language = 'bash'}) => {
  const template = typeof children === 'string' ? children : '';
  const [output, setOutput] = useState('');
  const [copied, setCopied] = useState(false);
  const update = useCallback(() => {
    if (typeof document === 'undefined') return;
    let s = template;
    inputIds.forEach((id, i) => {
      const el = document.getElementById(id);
      const v = el && el.value ? el.value : el ? el.placeholder || '' : '';
      s = s.split('$' + (i + 1)).join(v);
    });
    setOutput(s.trim());
  }, [template, inputIds]);
  useEffect(() => {
    update();
    const iv = setInterval(update, 400);
    inputIds.forEach(id => {
      const el = document.getElementById(id);
      if (el) el.addEventListener('input', update);
    });
    return () => {
      clearInterval(iv);
      inputIds.forEach(id => {
        const el = document.getElementById(id);
        if (el) el.removeEventListener('input', update);
      });
    };
  }, [update]);
  const copy = () => {
    navigator.clipboard.writeText(output).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  return <div className="rounded-xl overflow-hidden border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-900/50 my-4 shadow-sm">
      <div className="flex items-center justify-between px-4 py-2 border-b border-zinc-200 dark:border-zinc-700 bg-zinc-100/80 dark:bg-zinc-800/50 text-xs text-zinc-500 dark:text-zinc-400">
        <span className="font-medium uppercase tracking-wide">{language}</span>
        <button type="button" onClick={copy} className="px-3 py-1.5 rounded-md text-xs font-medium transition-colors bg-zinc-200/80 dark:bg-zinc-700/80 hover:bg-zinc-300 dark:hover:bg-zinc-600 text-zinc-700 dark:text-zinc-300 hover:text-zinc-900 dark:hover:text-zinc-100" style={copied ? {
    background: 'var(--mint-color-primary, #0057B7)',
    color: '#fff'
  } : {}}>{copied ? 'Copied!' : 'Copy'}</button>
      </div>
      <pre className="p-4 m-0 overflow-x-auto text-sm leading-relaxed font-mono whitespace-pre dark:!text-zinc-200 dark:!bg-zinc-900" style={{
    color: '#242628',
    backgroundColor: '#f8f9fa',
    minHeight: '2.5rem'
  }}>{output || ' '}</pre>
    </div>;
};

export const LiveCode = ({scopeId, fields}) => {
  const store = useRef(new Map());
  useEffect(() => {
    if (typeof document === 'undefined') return;
    const esc = s => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    const apply = () => {
      const root = document.getElementById(scopeId);
      if (!root) return;
      root.querySelectorAll('pre').forEach(b => {
        if (!store.current.has(b)) store.current.set(b, b.innerHTML);
        let html = store.current.get(b);
        fields.forEach(f => {
          const el = document.getElementById(f.id);
          const val = el ? el.value || el.placeholder || f.def : f.def;
          html = html.split(esc(f.def)).join(esc(val));
        });
        if (b.innerHTML !== html) b.innerHTML = html;
      });
    };
    apply();
    const iv = setInterval(apply, 250);
    const root = document.getElementById(scopeId);
    if (root) root.addEventListener('click', () => setTimeout(apply, 0));
    fields.forEach(f => {
      const el = document.getElementById(f.id);
      if (el) el.addEventListener('input', apply);
    });
    return () => {
      clearInterval(iv);
      fields.forEach(f => {
        const el = document.getElementById(f.id);
        if (el) el.removeEventListener('input', apply);
      });
    };
  }, [scopeId]);
  return null;
};

<Note>
  This page is for Authlete 3.0. For 2.x, see [this page](/v2/get-started/core-concepts/oauth-basics).
</Note>

# Preface

This tutorial describes the basic usage of [Authlete APIs](/api-reference) to implement an OAuth 2.0 authorization server that supports the authorization code grant flow. It also shows how Resource Servers can use Authlete APIs to quickly validate the access tokens, providing secure and authorized access to APIs.

**Prerequisites:**

* Basic knowledge of OAuth 2.0.
* Access to an Authlete account. [Sign up here if needed](https://console.authlete.com/register).

# Components

A typical OAuth 2.0 architecture involves multiple components, shown in the flowchart below. In this tutorial, we use the live, public cloud version of the [Authlete Management Console](https://console.authlete.com) and Authlete API Cluster running in the US region (`https://us.authlete.com`). The Authorization and Resource servers are simulated using `curl` commands etc., showing how an Authorization Server and Resource Server make API requests to Authlete for authorization, token issuance, and introspection.

```mermaid theme={null}
flowchart LR
    subgraph EndUser [End User]
        userAgent["User Agent (N/A)"]
        resourceOwner["Resource Owner (N/A)"]
    end

    subgraph APIClient [API Client]
        client["Client (N/A)"]
    end

    subgraph APIServer [API Server]
        authServer["Authorization Server (curl)"]
        resourceServer["Resource Server (curl)"]
        authAdmin["Authlete Administrator"]
    end

    subgraph Authlete [Authlete]
        mgmtConsole["Authlete Management Console"]
        authAPI["Authlete API"]
    end

    resourceOwner --> userAgent
    userAgent -- "Access to Client" --> client
    userAgent -- "Authorization Request (User Authentication and Consent)" --> authServer
    client -- "Token Request" --> authServer
    client -- "API Request" --> resourceServer

    %% API Server interactions with Authlete
    authAdmin -- "Service/Client Management" --> mgmtConsole
    mgmtConsole -- "API Request" --> authAPI

    authServer -- "API Request" --> authAPI
    resourceServer -- "API Request" --> authAPI

```

The FQDNs for each component are listed below. While the Authorization Server and Client are simulated, their FQDNs are used to illustrate the OAuth flow.

| Component                   | FQDN                   |
| --------------------------- | ---------------------- |
| Authlete API - US Cluster   | `us.authlete.com`      |
| Authlete Management Console | `console.authlete.com` |
| Authorization Server        | `as.example.com`       |
| Client                      | `client.example.org`   |
| Resource Server             | N/A                    |

# Environment Setup

Follow the [First API Call](/get-started/quickstarts/getting-started) to create a new Authlete Service and Client. Also, generate a Service Access Token, which is needed to call Authlete APIs. Enter your own values below, and the code/command samples on this page will update automatically. (In the SDK tabs, the service access token is read from the `SERVICE_ACCESS_TOKEN` environment variable rather than hard-coded.)

<InputTable
  col1="Item"
  col2="Value"
  rows={[
{ label: "Authlete API - US Cluster", id: "apiClusterHost", defaultValue: "https://us.authlete.com" },
{ label: "Service ID", id: "serviceID", defaultValue: "933860280" },
{ label: "Service Access Token", id: "serviceAccessToken", defaultValue: "DL7jo1z3iUIXyI5MnX" },
{ label: "Client ID", id: "clientID", defaultValue: "12818600553323" },
{ label: "Client Secret", id: "clientSecret", defaultValue: "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w" },
{ label: "Redirect URI", id: "redirectUri", defaultValue: "https://client.example.org/cb/example.com" },
{ label: "Client Type", static: "CONFIDENTIAL" },
{ label: "Client Authentication Method", static: "CLIENT_SECRET_BASIC" },
]}
/>

# Walk-through

The sequence diagram below shows the entire OAuth 2.0 flow as used in this tutorial. Refer back to it as you proceed through each step.

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant RO as Resource Owner
    participant UA as User Agent
    participant C as Client
    participant AS as Authorization Server
    participant RS as Resource Server
    participant API as Authlete API

    RO ->> UA: Start
    UA ->> C: Make a request
    C -->> UA: Authorization request
    UA ->> AS: Forward the request
    AS ->> API: POST /auth/authorization
    API -->> AS: OK to proceed<br/>(including ticket)
    AS -->> UA: User authentication and consent page
    RO <<->> UA: Enter credentials
    UA ->> AS: Login and consent
    AS ->> API: POST /auth/authorization/issue<br/>(with ticket)
    API -->> AS: Authorization response content<br/>(including authorization code)
    AS -->> UA: Authorization response
    UA ->> C: Forward the response
    C ->> AS: Token request<br/>(with authorization code)
    AS ->> API: POST /auth/token
    API -->> AS: Token response content<br/>(including access token)
    AS -->> C: Token response
    C ->> RS: Access protected resource<br/>(with access token)
    RS ->> API: POST /auth/introspection
    API -->> RS: Introspection response
    RS -->> C: Response to client
    C -->> UA: Content
    UA -->> RO: End
```

## Authorization request from the client to the authorization server

The client makes an authorization request to the authorization server via the user agent (Steps 3 and 4).
In this tutorial, we’ll assume the following values are specified as parameters in the request. The values from the **Environment Setup** inputs are reflected below.

<InputTable
  col1="Parameter"
  col2="Value"
  rows={[
{ code: "client_id", reflect: "clientID" },
{ code: "response_type", static: "code" },
{ code: "redirect_uri", reflect: "redirectUri" },
]}
/>

The authorization server is expected to receive the following content (folded for readability) as an HTTP GET query string from the user agent.

<DynamicCurl inputIds={['clientID', 'redirectUri']} language="shell">
  {`redirect_uri=$2\n &response_type=code\n &client_id=$1`}
</DynamicCurl>

A typical authorization server should evaluate the following rules before proceeding with the authorization code grant flow.

* Verify that a client associated with the client ID <TextReflect id="clientID" /> is registered with the authorization server.
* Check that the redirect URI <TextReflect id="redirectUri" /> matches one of the URIs registered for the client.
* Confirm that other parameter values, such as `response_type` and `scope`, are permitted for the client to specify in its request.

Authlete's [`/auth/authorization`](/api-reference/authorization-endpoint/process-authorization-request) API performs the above request validation on behalf of the authorization server.

Use sample command/code to make a request to the Authlete API (Step 5) by choosing one from the following language tab; the values will be updated as you edit the inputs above.

<div id="cg-authz">
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST "https://us.authlete.com/api/933860280/auth/authorization" \
         -H "Authorization: Bearer DL7jo1z3iUIXyI5MnX" \
         -H "Content-Type: application/json" \
         -d '{ "parameters": "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com"}'
    ```

    ```powershell PowerShell theme={null}
    $headers = @{ Authorization = "Bearer DL7jo1z3iUIXyI5MnX" }
    $body = '{ "parameters": "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com" }'
    Invoke-RestMethod -Method Post `
      -Uri "https://us.authlete.com/api/933860280/auth/authorization" `
      -Headers $headers -ContentType "application/json" -Body $body
    ```

    ```java Java theme={null}
    // authlete-java-common — initialize the client once and reuse it across the steps below.
    AuthleteConfiguration config = new AuthleteSimpleConfiguration()
            .setApiVersion("V3")
            .setBaseUrl("https://us.authlete.com")
            .setServiceApiKey("933860280")
            .setServiceAccessToken(System.getenv("SERVICE_ACCESS_TOKEN"));
    AuthleteApi authleteApi = AuthleteApiFactory.create(config);

    AuthorizationRequest request = new AuthorizationRequest()
            .setParameters("response_type=code"
                    + "&client_id=12818600553323"
                    + "&redirect_uri=https://client.example.org/cb/example.com");

    AuthorizationResponse response = authleteApi.authorization(request);
    System.out.println(response.getAction()); // INTERACTION
    System.out.println(response.getTicket());
    ```

    ```typescript TypeScript theme={null}
    import { Authlete } from "@authlete/typescript-sdk";

    // Initialize the client once and reuse it across the steps below.
    const authlete = new Authlete({
      serverURL: "https://us.authlete.com",
      bearer: process.env["SERVICE_ACCESS_TOKEN"] ?? "",
    });
    const SERVICE_ID = "933860280";

    const res = await authlete.authorization.processRequest({
      serviceId: SERVICE_ID,
      authorizationRequest: {
        parameters:
          "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com",
      },
    });
    console.log(res.action); // "INTERACTION"
    console.log(res.ticket);
    ```

    ```ruby Ruby theme={null}
    require "authlete_ruby_sdk"

    # Initialize the client once and reuse it across the steps below.
    client     = Authlete::Client.new(
      bearer:     ENV.fetch("SERVICE_ACCESS_TOKEN"),
      server_url: "https://us.authlete.com"
    )
    SERVICE_ID = "933860280"

    req = Authlete::Models::Components::AuthorizationRequest.new(
      parameters: "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com"
    )
    res = client.authorization.process_request(service_id: SERVICE_ID, authorization_request: req)

    puts res.authorization_response.action # "INTERACTION"
    puts res.authorization_response.ticket
    ```

    ```go Go theme={null}
    package main

    import (
    	"context"
    	"log"
    	"os"

    	authlete "github.com/authlete/authlete-go-sdk"
    	"github.com/authlete/authlete-go-sdk/models/components"
    )

    func main() {
    	ctx := context.Background()

    	// Initialize the client once and reuse it across the steps below.
    	s := authlete.New(
    		authlete.WithServerURL("https://us.authlete.com"),
    		authlete.WithSecurity(os.Getenv("SERVICE_ACCESS_TOKEN")),
    	)
    	const serviceID = "933860280"

    	res, err := s.Authorization.ProcessRequest(ctx, serviceID, components.AuthorizationRequest{
    		Parameters: "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com",
    	})
    	if err != nil {
    		log.Fatal(err)
    	}
    	log.Printf("action=%v", *res.AuthorizationResponse.Action) // INTERACTION
    	log.Printf("ticket=%s", *res.AuthorizationResponse.Ticket)
    }
    ```
  </CodeGroup>
</div>

<LiveCode
  scopeId="cg-authz"
  fields={[
{ id: "apiClusterHost", def: "https://us.authlete.com" },
{ id: "serviceID", def: "933860280" },
{ id: "serviceAccessToken", def: "DL7jo1z3iUIXyI5MnX" },
{ id: "clientID", def: "12818600553323" },
{ id: "redirectUri", def: "https://client.example.org/cb/example.com" },
]}
/>

If the request is valid, Authlete returns a response like this (Step 6).

```json theme={null}
{
    "action": "INTERACTION",
    "resultCode": "A004001",
    "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 933860280) for the authorization request from the client (ID = 12818600553323). [response_type=code, openid=false]",
    "ticket": "cElOaH9j4mS6AiIGR9oLqHlDn9jpvcNjqSgyRqfcmAE",
    "client": {...},
    "service": {...},
}
```

* `resultMessage` provides human-readable result of the request processing. (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes))
* `action` indicates what the authorization server should do next. (See also [Action Handling](/get-started/concepts/action-handling))
* `ticket` is required for the authorization server to make a request to another API in the following step. (See also [Two-Step API Calls](/get-started/concepts/two-step-api-calls))

Authlete also provides service and client information in the response. The authorization server utilizes them to ask the resource owner if he or she authorizes access for the client to the service.

## User authentication and confirmation of granting access

The actual interaction between the resource owner and the authorization server is out of scope for this tutorial. Typically, the authorization server would authenticate the user with credentials (e.g., ID and password), determine the user’s roles and privileges, and ask if they authorize access (Steps 7, 8 and 9).

## Issuing an authorization code

Assume the authorization server reaches the following state after completing the previous process:

* The authorization server has authenticated the resource owner and determined that the identifier to be shared with Authlete as the value of the `subject` parameter is `testuser01`.
* The authorization server has obtained consent from the resource owner.

The authorization server then makes a request to Authlete's [`/auth/authorization/issue`](/api-reference/authorization-endpoint/issue-authorization-response) to issue an authorization code. It includes the values of `subject` and `ticket` from the `/auth/authorization` API response as request parameters (Step 10). Enter your values below and the samples update automatically.

<InputTable
  col1="Item"
  col2="Value"
  rows={[
{ label: "Ticket", id: "ticket", defaultValue: "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc" },
{ label: "Subject", id: "subject", defaultValue: "testuser01" },
]}
/>

<div id="cg-issue">
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST "https://us.authlete.com/api/933860280/auth/authorization/issue" \
         -H "Authorization: Bearer DL7jo1z3iUIXyI5MnX" \
         -H "Content-Type: application/json" \
         -d '{ "ticket": "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc","subject": "testuser01"}'
    ```

    ```powershell PowerShell theme={null}
    $headers = @{ Authorization = "Bearer DL7jo1z3iUIXyI5MnX" }
    $body = '{ "ticket": "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc", "subject": "testuser01" }'
    Invoke-RestMethod -Method Post `
      -Uri "https://us.authlete.com/api/933860280/auth/authorization/issue" `
      -Headers $headers -ContentType "application/json" -Body $body
    ```

    ```java Java theme={null}
    // Reuse the authleteApi client created in the authorization step.
    AuthorizationIssueRequest request = new AuthorizationIssueRequest()
            .setTicket("5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc")
            .setSubject("testuser01");

    AuthorizationIssueResponse response = authleteApi.authorizationIssue(request);
    System.out.println(response.getAction());           // LOCATION
    System.out.println(response.getAuthorizationCode());
    System.out.println(response.getResponseContent());
    ```

    ```typescript TypeScript theme={null}
    // Reuse the `authlete` client and SERVICE_ID created in the authorization step.
    const res = await authlete.authorization.issue({
      serviceId: SERVICE_ID,
      authorizationIssueRequest: {
        ticket: "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc",
        subject: "testuser01",
      },
    });
    console.log(res.action);            // "LOCATION"
    console.log(res.authorizationCode);
    console.log(res.responseContent);
    ```

    ```ruby Ruby theme={null}
    # Reuse the `client` and SERVICE_ID created in the authorization step.
    req = Authlete::Models::Components::AuthorizationIssueRequest.new(
      ticket:  "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc",
      subject: "testuser01"
    )
    res = client.authorization.issue_response(service_id: SERVICE_ID, authorization_issue_request: req)

    puts res.authorization_issue_response.action            # "LOCATION"
    puts res.authorization_issue_response.authorization_code
    puts res.authorization_issue_response.response_content
    ```

    ```go Go theme={null}
    // Reuse the `s` client and serviceID created in the authorization step.
    res, err := s.Authorization.Issue(ctx, serviceID, components.AuthorizationIssueRequest{
    	Ticket:  "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc",
    	Subject: "testuser01",
    })
    if err != nil {
    	log.Fatal(err)
    }
    log.Printf("action=%v", *res.AuthorizationIssueResponse.Action) // LOCATION
    log.Printf("authorizationCode=%s", *res.AuthorizationIssueResponse.AuthorizationCode)
    log.Printf("responseContent=%s", *res.AuthorizationIssueResponse.ResponseContent)
    ```
  </CodeGroup>
</div>

<LiveCode
  scopeId="cg-issue"
  fields={[
{ id: "apiClusterHost", def: "https://us.authlete.com" },
{ id: "serviceID", def: "933860280" },
{ id: "serviceAccessToken", def: "DL7jo1z3iUIXyI5MnX" },
{ id: "ticket", def: "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc" },
{ id: "subject", def: "testuser01" },
]}
/>

If the request is valid, Authlete generates the following response (Step 11).

```json theme={null}
{
    "action": "LOCATION",
    "authorizationCode": "tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI",
    "idToken": null,
    "jwtAccessToken": null,
    "responseContent": "https://client.example.org/cb/example.com?code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&iss=https%3A%2F%2Fauthlete.com",
    "resultCode": "A040001",
    "resultMessage": "[A040001] The authorization request was processed successfully.",
    "ticketInfo": {
        "context": null
    }
}
```

* `resultMessage` provides a human-readable result of the request processing. (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes))
* `action` indicates what the authorization server should do next. The value in this response is `LOCATION`, meaning the authorization server should make a redirection response back to the user agent. (See also [Action Handling](/get-started/concepts/action-handling))
* `responseContent` represents the content of the response from the authorization server. (See also [Action Handling](/get-started/concepts/action-handling))

The authorization server is expected to send the following response (folded for readability) to the user agent (Step 12).

```http theme={null}
HTTP/1.1 302 Found
Location: https://client.example.org/cb/example.com
 ?code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI
```

There are cases where the authorization server may decide not to issue tokens to the client due to the result of the previous authentication, user consent confirmation, or other missing context that prohibits token issuance. In these situations, the authorization server must inform the client that the authorization flow has been terminated with an appropriate protocol response.

Authlete's  [`/auth/authorization/fail`](/api-reference/authorization-endpoint/fail-authorization-request) API supports the termination process in terms of messages to be sent to the client, and transfer method for the response.

To summarize, an authentication server usually makes a request to either `/auth/authorization/issue` or `/auth/authorization/fail` API depending on result of user authentication and consent.

## Token request

Here, we assume that the user agent receives the redirection response from the authorization server. It then sends the following request (folded for readability) to the client (Step  13). Enter the **authorization code** (the `code` from the previous response) below and the samples update.

<InputTable
  col1="Item"
  col2="Value"
  rows={[
{ label: "Authorization code (code)", id: "authzCode", defaultValue: "tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI" },
]}
/>

<DynamicCurl inputIds={['authzCode']} language="http">
  {`GET /cb/example.com?code=$1 HTTP/1.1\nHost: client.example.org`}
</DynamicCurl>

The client extracts the value of the `code` parameter, crafts a token request using this value, and sends it to the authorization server as follows (folded for readability). In this tutorial, `https://as.example.com/token` is the token endpoint URI (Step 14).

<DynamicCurl inputIds={['clientID', 'clientSecret', 'authzCode', 'redirectUri']} language="http">
  {`POST /token HTTP/1.1\nHost: as.example.com\nAuthorization: Basic base64($1:$2)\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=authorization_code\n &code=$3\n &redirect_uri=$4`}
</DynamicCurl>

The authorization server evaluates the parameters in the request and then sends a token response back to the client.
In this tutorial, we’ll use Authlete's [`/auth/token`](/api-reference/token-endpoint/process-token-request) API to evaluate the request and generate the response.

<div id="cg-token">
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST "https://us.authlete.com/api/933860280/auth/token" \
         -H "Authorization: Bearer DL7jo1z3iUIXyI5MnX" \
         -H "Content-Type: application/json" \
         -d '{ "clientId": "12818600553323","clientSecret": "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w","parameters": "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com" }'
    ```

    ```powershell PowerShell theme={null}
    $headers = @{ Authorization = "Bearer DL7jo1z3iUIXyI5MnX" }
    $body = '{ "clientId": "12818600553323", "clientSecret": "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w", "parameters": "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com" }'
    Invoke-RestMethod -Method Post `
      -Uri "https://us.authlete.com/api/933860280/auth/token" `
      -Headers $headers -ContentType "application/json" -Body $body
    ```

    ```java Java theme={null}
    // Reuse the authleteApi client created in the authorization step.
    TokenRequest request = new TokenRequest()
            .setClientId("12818600553323")
            .setClientSecret("AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w")
            .setParameters("grant_type=authorization_code"
                    + "&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI"
                    + "&redirect_uri=https://client.example.org/cb/example.com");

    TokenResponse response = authleteApi.token(request);
    System.out.println(response.getAction());          // OK
    System.out.println(response.getAccessToken());
    System.out.println(response.getResponseContent());
    ```

    ```typescript TypeScript theme={null}
    // Reuse the `authlete` client and SERVICE_ID created in the authorization step.
    const res = await authlete.token.process({
      serviceId: SERVICE_ID,
      tokenRequest: {
        clientId: "12818600553323",
        clientSecret:
          "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w",
        parameters:
          "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com",
      },
    });
    console.log(res.action);          // "OK"
    console.log(res.accessToken);
    console.log(res.responseContent);
    ```

    ```ruby Ruby theme={null}
    # Reuse the `client` and SERVICE_ID created in the authorization step.
    req = Authlete::Models::Components::TokenRequest.new(
      client_id:     "12818600553323",
      client_secret: "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w",
      parameters:    "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com"
    )
    res = client.tokens.process_request(service_id: SERVICE_ID, token_request: req)

    puts res.token_response.action           # "OK"
    puts res.token_response.access_token
    puts res.token_response.response_content
    ```

    ```go Go theme={null}
    // Reuse the `s` client and serviceID created in the authorization step.
    res, err := s.Token.Process(ctx, serviceID, components.TokenRequest{
    	ClientID:     types.String("12818600553323"), // types: github.com/authlete/authlete-go-sdk/types
    	ClientSecret: types.String("AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w"),
    	Parameters:   "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com",
    })
    if err != nil {
    	log.Fatal(err)
    }
    log.Printf("action=%v", *res.TokenResponse.Action) // OK
    log.Printf("accessToken=%s", *res.TokenResponse.AccessToken)
    log.Printf("responseContent=%s", *res.TokenResponse.ResponseContent)
    ```
  </CodeGroup>
</div>

<LiveCode
  scopeId="cg-token"
  fields={[
{ id: "apiClusterHost", def: "https://us.authlete.com" },
{ id: "serviceID", def: "933860280" },
{ id: "serviceAccessToken", def: "DL7jo1z3iUIXyI5MnX" },
{ id: "clientID", def: "12818600553323" },
{ id: "clientSecret", def: "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w" },
{ id: "authzCode", def: "tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI" },
{ id: "redirectUri", def: "https://client.example.org/cb/example.com" },
]}
/>

If the request is valid, Authlete responds as follows (Step 16).

```json theme={null}
{
    "accessToken": "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g",
    "accessTokenDuration": 86400,
    "accessTokenExpiresAt": 1730203012061,
    "action": "OK",
    "clientAuthMethod": "CLIENT_SECRET_BASIC",
    "clientId": 12818600553323,
    "grantType": "AUTHORIZATION_CODE",
    "refreshToken": "1mQRm2XRwaBNYRIjxjO0ls9hb4eDKo9I3ChU0yy8lHo",
    "refreshTokenDuration": 864000,
    "refreshTokenExpiresAt": 1730980612061,
    "responseContent": "{\"access_token\":\"gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g\",\"token_type\":\"Bearer\",\"expires_in\":86400,\"scope\":null,\"refresh_token\":\"1mQRm2XRwaBNYRIjxjO0ls9hb4eDKo9I3ChU0yy8lHo\"}",
    "resultCode": "A050001",
    "resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
    "subject": "testuser01",
}
```

* `resultMessage` provides a human-readable result of the request processing. (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes))
* `action` indicates what the authorization server should do next. The value in this response is `OK`, meaning the authorization server should send a token response back to the client. (See also [Action Handling](/get-started/concepts/action-handling))
* `responseContent` contains the content of the response from the authorization server. (See also [Action Handling](/get-started/concepts/action-handling))

The authorization server is expected to send the following response to the client (Step 17).

```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token":"gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g",
  "refresh_token":"1mQRm2XRwaBNYRIjxjO0ls9hb4eDKo9I3ChU0yy8lHo",
  "scope":null,
  "token_type":"Bearer",
  "expires_in":86400
}
```

<Info>The authorization server has now successfully created the tokens and provided them to the client. By leveraging Authlete APIs, the authorization server avoids the need to implement complex logic to evaluate parameters in authorization and token requests and can respond correctly using the appropriate methods.</Info>

## API request (access token introspection)

In most cases, the client sends a request with the access token to the resource server providing the APIs (Step 18). The resource server is responsible for evaluating the token's validity, retrieving information about the user and client associated with the token, and determining how to respond to the API request.

Authlete provides the [`/auth/introspection`](/api-reference/introspection-endpoint/process-introspection-request) API for this purpose. It verifies the token's validity and provides the necessary information.

Enter the access token received by the resource server below, then use the following command to introspect it using the Authlete API.

<InputTable
  col1="Item"
  col2="Value"
  rows={[
{ label: "Access token (access_token)", id: "accessToken", defaultValue: "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g" },
]}
/>

<div id="cg-introspect">
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST "https://us.authlete.com/api/933860280/auth/introspection" \
         -H "Authorization: Bearer DL7jo1z3iUIXyI5MnX" \
         -H "Content-Type: application/json" \
         -d '{ "token": "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g" }'
    ```

    ```powershell PowerShell theme={null}
    $headers = @{ Authorization = "Bearer DL7jo1z3iUIXyI5MnX" }
    $body = '{ "token": "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g" }'
    Invoke-RestMethod -Method Post `
      -Uri "https://us.authlete.com/api/933860280/auth/introspection" `
      -Headers $headers -ContentType "application/json" -Body $body
    ```

    ```java Java theme={null}
    // Reuse the authleteApi client created in the authorization step.
    IntrospectionRequest request = new IntrospectionRequest()
            .setToken("gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g");

    IntrospectionResponse response = authleteApi.introspection(request);
    System.out.println(response.getAction());   // OK
    System.out.println(response.isUsable());     // true
    System.out.println(response.getSubject());   // testuser01
    System.out.println(response.getClientId());  // 12818600553323 (long)
    ```

    ```typescript TypeScript theme={null}
    // Reuse the `authlete` client and SERVICE_ID created in the authorization step.
    const res = await authlete.introspection.process({
      serviceId: SERVICE_ID,
      introspectionRequest: {
        token: "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g",
      },
    });
    console.log(res.action);   // "OK"
    console.log(res.subject);  // "testuser01"
    console.log(res.clientId); // 12818600553323 (number)
    ```

    ```ruby Ruby theme={null}
    # Reuse the `client` and SERVICE_ID created in the authorization step.
    req = Authlete::Models::Components::IntrospectionRequest.new(
      token: "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g"
    )
    res = client.introspection.process_request(service_id: SERVICE_ID, introspection_request: req)

    puts res.introspection_response.action    # "OK"
    puts res.introspection_response.subject   # "testuser01"
    puts res.introspection_response.client_id # 12818600553323 (Integer)
    ```

    ```go Go theme={null}
    // Reuse the `s` client and serviceID created in the authorization step.
    res, err := s.Introspection.Process(ctx, serviceID, components.IntrospectionRequest{
    	Token: "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g",
    })
    if err != nil {
    	log.Fatal(err)
    }
    log.Printf("action=%v", *res.IntrospectionResponse.Action)    // OK
    log.Printf("subject=%s", *res.IntrospectionResponse.Subject)  // testuser01
    log.Printf("clientId=%d", *res.IntrospectionResponse.ClientID) // 12818600553323 (int64)
    ```
  </CodeGroup>
</div>

<LiveCode
  scopeId="cg-introspect"
  fields={[
{ id: "apiClusterHost", def: "https://us.authlete.com" },
{ id: "serviceID", def: "933860280" },
{ id: "serviceAccessToken", def: "DL7jo1z3iUIXyI5MnX" },
{ id: "accessToken", def: "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g" },
]}
/>

If the request is valid, Authlete returns the following response (Step 20).

```json theme={null}
{
    "action": "OK",
    "authTime": 0,
    "clientId": 12818600553323,
    "expiresAt": 1730203012000,
    "grantType": "AUTHORIZATION_CODE",
    "issuableCredentials": null,
    "properties": null,
    "refreshable": true,
    "resources": null,
    "responseContent": "Bearer error=\"invalid_request\"",
    "resultCode": "A056001",
    "resultMessage": "[A056001] The access token is valid.",
    "scopeDetails": null,
    "scopes": null,
    "serviceAttributes": null,
    "subject": "testuser01",
    "sufficient": false,
    "usable": true
}
```

The resource server can retrieve various information, such as the token’s expiration time (`expiresAt`), the identifier of the user who approved access (`subject`), the grant type used to obtain the token, and the client identifier (`clientId`). It then determines how to respond to the API request (Step 21).

# Recap

In this tutorial, we covered how to use Authlete APIs to implement the authorization code grant flow in an authorization server.

# Next Steps

Let’s dig deeper into Authlete by exploring the following features.

* [Authlete API Tutorial - OIDC Basics](/get-started/quickstarts/oidc-basics)
* [PKCE (RFC 7636)](/protocols-and-flows/protocol-extensions/proof-key-for-code-exchange-pkce)
* [Authorization Server Reference Implementations](https://github.com/authlete/java-oauth-server)
