> For the complete documentation index, see [llms.txt](https://docs.tpi.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tpi.dev/api-integration-guide/using-token-for-authentication-and-authorization/openid-connect-oidc-mode.md).

# OpenID Connect (OIDC) Mode

### **Workflow Overview** <a href="#toc163842061" id="toc163842061"></a>

#### **Higher-Security Process (Client Password Required)**

![](https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/RIBtu6rmAvmVMz4l6Kfh/0.png)

![](https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/pJinbLH1SdhkGPrOYjkh/1.png)

#### **PKCE + Public Client Process (Client Password Not Required)**

![](https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/kygsDn8nFP0iSjZMa9U8/2.png)

![](https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/DKn17izFCZa4sNxSI4Xl/3.png)

![](https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/6Sfzgb4qpheHlZ4F6aJ7/4.png)

### **Setting up Callback**

Once the user completes authentication and authorization, the authorization code and state will be sent to the **Default Redirect URL** (callback).

![](https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/22aNfODidVnRdZR8EzyV/5.png)

When using a Public Client, select **Public Client (With PKCE)** in the **OAuth grant type**<mark style="color:red;">**\***</mark> field. The PKCE feature is applicable regardless of whether this option is selected. However, selecting **Public Client (With PKCE)** allows for the omission of a client password, as PKCE is required for a Public Client.

The PKCE specification adds parameters such as `code_challenge_method`, `code_challenge`, and `code_verifier` to verify that the request origin is consistent, allowing authorization to be completed without a client password. Although the process becomes more complex, it is one of the most secure modes available.

![](https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/eiNi16ZjLY0SJWmv7FWN/6.png)

### **Initiating Authorization Requests and Validating Users** <a href="#toc163842063" id="toc163842063"></a>

Initiate the process of authenticating LDAP/AD users and authorizing your application using the digiRunner platform. When the user clicks **Login**, they will be redirected to the authorization URL.

Example URL:

```
https://{digiRunner_DOMAIN}/dgrv4/ssotoken/gtwidp/LDAP/authorization?
response_type=code&client_id=
{client_id}&scope=openid%20profile%20email&redirect_uri=
https://example.com/callback&state={state}&code_challenge=
{code_challenge}&code_challenge_method=S256
```

You can send the following parameters to the authorization URL.

<table><thead><tr><th width="192">Parameter</th><th width="78">Type</th><th width="129">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>response_type</code></td><td>String</td><td>Required</td><td>code, a fixed value</td></tr><tr><td><code>client_id</code></td><td>String</td><td>Required</td><td>Client ID registered with digiRunner</td></tr><tr><td><code>scope</code></td><td>String</td><td>Required</td><td>OIDC access scope; if there are multiple, separate them with "<em>%20</em>"</td></tr><tr><td><code>redirect_uri</code></td><td>String</td><td>Required</td><td>Redirect URL (callback) registered by the client with digiRunner</td></tr><tr><td><code>state</code></td><td>String</td><td>Required</td><td>Random code, a unique alphanumeric string used to prevent cross-site request forgery (CSRF) attacks. Your application should generate a random value, such as a UUID, for each login session.</td></tr><tr><td><code>code_challenge</code></td><td>String</td><td>Optional</td><td><p><strong>Required only when using PKCE authentication.</strong></p><p>KADwy…, a random string with a length of 43~128 characters, with the string as <em>code_verifier</em>, calculate <em>code_challenge</em> with SHA256 and Base64URL Encode: <strong>code_challenge = Base64URL Encode(SHA256(code_verifier))</strong></p><p>Example:</p><p><strong>code_verifier:</strong> <code>B7gB0cY1C58ecNJ2J-231Ep-NmXgghAzgZg9nXu-vDo</code></p><p><strong>code_challenge:</strong> <code>Jhlf18b9aDFC5hkgQy3_MO1MznyS7kqMi32wELbhdos</code></p></td></tr><tr><td><code>code_challenge_method</code></td><td>String</td><td>Optional</td><td><p><strong>Required only when using PKCE authentication.</strong></p><p><code>S256</code>, a fixed value</p></td></tr></tbody></table>

### **User Login with LDAP/AD** <a href="#toc163842064" id="toc163842064"></a>

<div align="left"><img src="https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/LpnFdN3nkRYm7UbMWLkW/7.png" alt="" width="375"></div>

### **User Authorization** <a href="#toc163842065" id="toc163842065"></a>

<div align="left"><img src="https://content.gitbook.com/content/0choIC1BbdPOkKWOYGgk/blobs/oQZn7wiQ3wPfbCs0xwra/8.png" alt="" width="375"></div>

### **Receiving Authorization Code or Error Response**

After completing the authentication and authorization process, the user will be redirected to the Redirect URL (callback). If the user has granted permission to access your application, an authorization code will be returned.If the user has not authorized access to your application, an error response will be returned.

#### **Receiving Authorization Code**

When the user successfully completes the authentication and authorization steps,they will be redirected to the Redirect URL (callback) with the following parameters.

Example URL of the redirect target:

```
https://example.com/callback?code={code}&state={state}
```

Parameters with details:

<table><thead><tr><th width="129">Parameter</th><th width="79">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>code</code></td><td>String</td><td>Authorization code for acquiring the access token; valid once for 10 minutes</td></tr><tr><td><code>state</code></td><td>String</td><td>If the request includes the state parameter, the same value should appear in the response. The application must verify if the state value in the request matches the one in the response.</td></tr></tbody></table>

#### **Receiving Error Response**

If the user declines to grant permissions to your application request, they will be redirected to the Redirect URL (callback) with the following parameters.

Example URL of the redirect target:

```
https://example.com/callback?rtn_code=cancel&msg=VXNlciBwcmVzc2VzIGNhbmNlbA
```

Parameters with details:

<table><thead><tr><th width="137">Parameter</th><th width="80">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>rtn_code</code></td><td>String</td><td>Error code (Types as shown in the table below)</td></tr><tr><td><code>msg</code></td><td>String</td><td>Error message. TSP must use Base64URL Decode to obtain the error message, assisting developers in identifying specific error messages for authentication error reasons. This section contains most of the information about why the error occurred.</td></tr></tbody></table>

<table><thead><tr><th width="137">Error Code</th><th width="299">Description</th><th>User Action</th></tr></thead><tbody><tr><td><code>error</code></td><td>Disallowed application requesting an authorization code</td><td>User account or password error, or other errors</td></tr><tr><td><code>cancel</code></td><td>Resource owner denied consent</td><td>User pressed cancel on the consent screen</td></tr></tbody></table>

### **Obtaining Access Token / Refresh Token / ID Token** <a href="#toc163842067" id="toc163842067"></a>

Example request:

```
curl -v -X POST https://{digiRunner_DOMAIN}/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Authorization: Basic {client_secret}' \
-d 'grant_type=authorization_code' \
-d 'code={code}' \
-d 'redirect_uri=https://example.com/callback'
-d 'code_verifier={code_verifier}'
```

Parameters with details:

<table><thead><tr><th width="183">Parameter</th><th width="79">Type</th><th width="131">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>client_secret</code></td><td>String</td><td>Required</td><td><p><strong>Scenario I. Higher-Security (Client Password Required)</strong></p><p>1. Client Password registered with digiRunner; needs to be encoded with Base64</p><p></p><p>2. Concatenate the client ID with ":" and the encoded client password, then encode the result with Base64 again to generate the value to be used</p><p></p><p>3. Example:</p><p>Client ID: tspclient</p><p>Client Password: tsp123abcd</p><p></p><p>Formula for generating the value:</p><p>Base64 Encode(ClientID+":"+Base64 Encode(Client Password))</p><p>Base64 Encode(tspclient+":"+Base64 Encode(tsp123abcd))</p><p>Base64 Encode(tspclient:dHNwMTIzYWJjZA==)</p><p></p><p>Output for client_secret:</p><p>dHNwY2xpZW50OmRITndNVEl6WVdKalpBPT0=</p><p></p><p><strong>Scenario II. PKCE + Public Client (Client Password Not Required)</strong></p><p>When using PKCE and the <strong>Public Client (With PKCE)</strong> option is selected in digiRunner's <strong>OAuth grant type</strong>, the client password is not required.</p><p></p><p>1. Concatenate the client ID with ":" (no client password is required)</p><p></p><p>2. Example:</p><p>Client ID: tspclient</p><p>Client Password: ""</p><p></p><p>Formula for generating the value:</p><p>Base64 Encode(ClientID+":")</p><p>Base64 Encode(tspclient+":")</p><p>Base64 Encode(tspclient:)</p><p></p><p>Output for client_secret:</p><p>dHNwY2xpZW50Og==</p></td></tr><tr><td><code>grant_type</code></td><td>String</td><td>Required</td><td><code>authorization_code</code>, a fixed value used when making API calls</td></tr><tr><td><code>code</code></td><td>String</td><td>Required</td><td>Authorization code, a unique value that changes every time</td></tr><tr><td><code>redirect_uri</code></td><td>String</td><td>Required</td><td>Redirect URL (callback) registered by the client with digiRunner, for verification purposes</td></tr><tr><td><code>code_verifier</code></td><td>String</td><td>Optional</td><td><p><strong>Required only when using PKCE authentication.</strong></p><p>The <em>code_verifier</em> generated from the previous authorization request (<em>/authorization</em>).</p><p>Example:</p><p><strong>code_verifier:</strong> <code>B7gB0cY1C58ecNJ2J-231Ep-NmXgghAzgZg9nXu-vDo</code></p><p><strong>code_challenge:</strong> <code>Jhlf18b9aDFC5hkgQy3_MO1MznyS7kqMi32wELbhdos</code></p></td></tr></tbody></table>

Example of successful response:

```
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJub2RlIjoiZXhlY3V0b3IxIiwiYXVkIjpbIllXUnRhVzVCVUVrIl0sInVzZXJfbmFtZSI6Im1pbmlsZGFwIiwic2NvcGUiOlsib3BlbmlkIiwiZW1haWwiLCJwcm9maWxlIiwiMjAwMDAwMDA4NiIsIjIwMDAwMDAwODgiXSwic3RpbWUiOj...",
"expires_in": 1799,
"jti": "ea61ff17-ff8e-43dd-bfc7-5a705ce75aa8",
"node": "executor1",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJub2RlIjoiZXhlY3V0b3IxIiwiYXVkIjpbIllXUnRhVzVCVUVrIl0sInVzZXJfbmFtZSI6Im1pbmlsZGFwIiwic2NvcGUiOlsib3BlbmlkIiwiZW1haWwiLCJwcm9maWxlIiwiMjAwMDAwMDA4NiIsIjIwMDAwMDAwODgiXSwiYXRpIjoiZW...",
"scope": "openid email profile 2000000086 2000000088",
"stime": 1684812470001,
"token_type": "bearer",
"idp_type": "LDAP",
"id_token": "eyJraWQiOiIzOWY5Y2Q4NC0zNGJkLTRmZDgtODRjNy04Njc2ZjlmZjc0MWUiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiMmYwQWxoUDY5N01pZTl5bkIzb3lqUSIsImF1ZCI6Im1pbmlsZGFwY2xpZW50Iiwic3ViIjoibWluaWxkYXAiLCJpc3MiOiJodHRwczovLzEw..."
}
```

<table><thead><tr><th width="185">Parameter</th><th width="98">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>access_token</code></td><td>String</td><td>Access token (JWT)</td></tr><tr><td><code>expires_in</code></td><td>Number</td><td>Time duration (unit: seconds) before the access token expires</td></tr><tr><td><code>jti</code></td><td>String</td><td>Access token ID</td></tr><tr><td><code>node</code></td><td>Number</td><td>Identifier compatible with digiRunner v3</td></tr><tr><td><code>refresh_token</code></td><td>String</td><td>Refresh token (JWT), used to obtain a new access token</td></tr><tr><td><code>scope</code></td><td>String</td><td>Permissions granted by the user</td></tr><tr><td><code>stime</code></td><td>Number</td><td>Start time of the access token issuance (unit: milliseconds).</td></tr><tr><td><code>token_type</code></td><td>String</td><td>Bearer token</td></tr><tr><td><code>idp_type</code></td><td>String</td><td>LDAP</td></tr><tr><td><code>id_token</code></td><td>String</td><td>ID token (JWT), which includes information about the user; this field is returned only when the scope specifies OpenID, email, or profile.</td></tr></tbody></table>

### **Token Renewal with Cookie Token** <a href="#toc163842068" id="toc163842068"></a>

After user authentication is completed, the user will receive a response containing the access token and refresh token. The jti and idp\_type values of the access token will be recorded in the browser's cookie.

When the user close the browser without logging out and later re-enter the TSP webpage, he can use cookies to obtain a new access token. This allows the user to access the TSP system without having to log in again.

Example request:

```
curl -v -X POST https://{digiRunner_DOMAIN}/oauth/token \
-H 'Accept: application/json' \
-H 'Cookie: idp_type=LDAP; jti={jti}'
```

Parameters with details:

<table><thead><tr><th width="149">Parameter</th><th width="85">Type</th><th width="131">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>idp_type</code></td><td>String</td><td>Required</td><td>LDAP, the value of idp_type recorded in the browser cookie</td></tr><tr><td><code>jti</code></td><td>String</td><td>Required</td><td>Value of the access token’s jti recorded in the browser cookie</td></tr></tbody></table>

Example of successful response:

```
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJub2RlIjoiZXhlY3V0b3IxIiwiYXVkIjpbIllXUnRhVzVCVUVrIl0sInVzZXJfbmFtZSI6Im1pbmlsZGFwIiwic2NvcGUiOlsib3BlbmlkIiwiZW1haWwiLCJwcm9maWxlIiwiMjAwMDAwMDA4NiIsIjIwMDAwMDAwODgiXSwic3RpbWUiOj...",
"expires_in": 1799,
"jti": "815f60d2-9c1d-4402-bbf0-60df49e94bef",
"node": "executor1",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJub2RlIjoiZXhlY3V0b3IxIiwiYXVkIjpbIllXUnRhVzVCVUVrIl0sInVzZXJfbmFtZSI6Im1pbmlsZGFwIiwic2NvcGUiOlsib3BlbmlkIiwiZW1haWwiLCJwcm9maWxlIiwiMjAwMDAwMDA4NiIsIjIwMDAwMDAwODgiXSwiYXRpIjoiOD...",
"scope": "openid email profile 2000000086 2000000088",
"stime": 1684824569848,
"token_type": "bearer",
"idp_type": "LDAP",
"id_token": "eyJraWQiOiJmODE4ZDk3MS04ZGJiLTQ3YjMtOTI5MS1mNDJiMWVhMDZlMTgiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiRzQ3dEQ2WVd5X0RGaWNyZzZjc200dyIsImF1ZCI6Im1pbmlsZGFwY2xpZW50Iiwic3ViIjoibWluaWxkYXAiLCJpc3MiOiJodHRwczovLzEw..."
```

If the jti (access token ID) corresponds to an expired refresh token, a 401 Unauthorized HTTP status code and JSON response will be returned.

Example of error response:

```
{
"error": "invalid_token",
"error_description": "Refresh token expired, exp: 1684910969, expired at: 2023/05/17 09:47:31.000"
}
```

### **Calling APIs Registered in digiRunner with Access Token** <a href="#calling_apis_registered" id="calling_apis_registered"></a>

After obtaining the access token issued by digiRunner, TSP can utilize the associated permissions to call APIs registered in digiRunner.

Example request:

```
curl -v -X POST 'https://{digiRunner_DOMAIN}/tsmpc/tspTest/postApi' \
-H 'Authorization: Bearer {access_token}'
```

Parameters with details:

<table><thead><tr><th width="173">Parameter</th><th width="80">Type</th><th width="135">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>access_token</code></td><td>String</td><td>Required</td><td>Access token (JWT)</td></tr></tbody></table>

### **Calling APIs Registered in digiRunner with ID Token** <a href="#toc163842070" id="toc163842070"></a>

Example request:

```
curl -v -X POST 'https://{digiRunner_DOMAIN}/tsmpc/tspTest/postApi' \
-H 'ID-Token: {id_token}' \
-H 'Authorization: Bearer {access_token}'
```

Parameters with details:

<table><thead><tr><th width="176">Parameter</th><th width="94">Type</th><th width="134">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>id_token</code></td><td>String</td><td>Required</td><td>ID token (JWT)</td></tr><tr><td><code>access_token</code></td><td>String</td><td>Required</td><td>Access token (JWT)</td></tr></tbody></table>

#### **Verifying ID Token through Custom Implementation**

The ID token is a JSON Web Token (JWT) containing user information. Attackers may attempt to forge ID tokens. When your backend server receives an ID token from digiRunner, you should verify the authenticity of the ID token. You can refer to the journal [How to Apply ID Token (ID Token 如何應用)](https://www.tpisoftware.com/tpu/articleDetails/2919) on TPIsoftware University, use [JWT libraries](https://jwt.io/#libraries-io) or implement your own validator.

#### **Verifying ID Token by Calling an API**

Using the methods mentioned above or invoking an API to verify the received ID token for its legitimacy and obtain the user's personal information and email.

Example request:

```
curl -v -X POST 'https://{digiRunner_DOMAIN}/dgrv4/ssotoken/gtwidp/verify' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'id_token={id_token}'
```

Parameters with details:

<table><thead><tr><th width="145">Parameter</th><th width="83">Type</th><th width="131">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>id_token</code></td><td>String</td><td>Required</td><td>ID token (JWT)</td></tr></tbody></table>

Example of successful response:

```
{
"iss": "https://10.20.30.88:18442/dgrv4/ssotoken/LDAP",
"sub": "tspldapuser",
"aud": "tspldapclient",
"exp": 1686555619,
"iat": 1686555019,
"name": "Tsp Lee",
"email": "tsp.lee@tpisoftware.com"
}
```

Parameters with details:

<table><thead><tr><th width="128">Parameter</th><th width="100">Type</th><th width="133">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>iss</code></td><td>String</td><td>Required</td><td>Issuer of the ID token</td></tr><tr><td><code>sub</code></td><td>String</td><td>Required</td><td>User Name</td></tr><tr><td><code>aud</code></td><td>String</td><td>Required</td><td>digiRunner's client ID</td></tr><tr><td><code>exp</code></td><td>Number</td><td>Required</td><td>Expiration date of the ID token</td></tr><tr><td><code>iat</code></td><td>Number</td><td>Required</td><td>Time of ID token generation</td></tr><tr><td><code>name</code></td><td>String</td><td>Optional</td><td>User's display name<br>If the profile scope is not specified in the authorization request, it will not be included.</td></tr><tr><td><code>picture</code></td><td>String</td><td>Optional</td><td>User's profile picture URL<br>If the profile scope is not specified in the authorization request, it will not be included.</td></tr><tr><td><code>email</code></td><td>String</td><td>Optional</td><td>User's email address<br>If the email scope is not specified in the authorization request, it will not be included.</td></tr></tbody></table>

If the ID token has expired, a 400 Bad Request HTTP status code and JSON response will be returned.

Example of error response:

```
{
"error": "invalid_request",
"error_description": "Expired JWT"
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.tpi.dev/api-integration-guide/using-token-for-authentication-and-authorization/openid-connect-oidc-mode.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
