# Integrating LINE Login with your web app

{% hint style="warning" %}
Ensure you've thoroughly reviewed the official [LINE Login integration documentation](https://developers.line.biz/en/docs/line-login/integrate-line-login/) before proceeding.
{% endhint %}

### **Prerequisites**

* Familiarity with the LINE Login integration documentation.
* Access to your LINE Developers Console.
* Basic knowledge of web development.

{% hint style="info" %}
**Before you start**

Ensure that your web app has been correctly integrated with the Antsomi Website SDK. If it hasn't, please refer to the [documentation](https://docs.antsomi.com/developers-guide/website/getting-started) before proceeding further.
{% endhint %}

### Preparation

Now that you're equipped with the prerequisite knowledge, let's prepare your web application for LINE Login integration.

{% tabs %}
{% tab title="HTML" %}

```html
<script>
    async function generateCodeChallenge(codeVerifier) {
        var digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(codeVerifier));
    
        return btoa(String.fromCharCode(...new Uint8Array(digest))).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
    }
    
    function generateRandomString(length) {
        var text = "";
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    
        for (var i = 0; i < length; i++) {
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        }
    
        return text;
    }
</script>
```

{% endtab %}
{% endtabs %}

### **Initialization**

Initiate the integration process by setting up the necessary parameters and code for a smooth LINE Login experience in your web app.

{% tabs %}
{% tab title="HTML" %}

```html
<script>
    document.addEventListener("DOMContentLoaded", function (event) {
        var codeVerifier = generateRandomString(64);
        const challengeMethod = crypto.subtle ? "S256" : "plain";

        Promise.resolve()
            .then(() => {
                if (challengeMethod === 'S256') {
                    return generateCodeChallenge(codeVerifier);
                } 
                else {
                    return codeVerifier;
                }
            })
            .then(function (codeChallenge) {
                window.sessionStorage.setItem("code_verifier", codeVerifier);

                var redirectUri = window.location.href.split('?')[0];
                var args = new URLSearchParams({
                    response_type: "code",
                    client_id: "<YOUR_CLIENT_ID>",
                    code_challenge_method: challengeMethod,
                    code_challenge: codeChallenge,
                    redirect_uri: redirectUri,
                    state: "12345",
                    scope: "profile openid"
                });
                window.location = "https://access.line.me/oauth2/v2.1/authorize/?" + args;
            });
    });
</script>

```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Replace `"<YOUR_CLIENT_ID>"` with your actual LINE Login channel's client ID.
{% endhint %}

### **Token Exchange**

Once initialized, proceed with the token exchange phase, where authentication codes are exchanged for access tokens, paving the way for seamless user interactions.

{% tabs %}
{% tab title="HTML" %}

```html
<script>
    const authorizeEndpoint = "https://access.line.me/oauth2/v2.1/authorize";
    const tokenEndpoint = "https://api.line.me/oauth2/v2.1/token";
    const userEnpoint = "https://api.line.me/v2/profile";
    const clientId = "<YOUR_CLIENT_ID>";

    if (window.location.search) {
        var args = new URLSearchParams(window.location.search);
        var code = args.get("code");

        if (code) {
            var xhr = new XMLHttpRequest();

            xhr.onload = function () {
                var response = xhr.response;
                var message;

                if (xhr.status == 200) {
                    // Token exchange successful
                    message = "Access Token: " + JSON.stringify(response);
                }
                else {
                    // Token exchange error
                    message = "Error: " + response.error_description + " (" + response.error + ")";
                }

                // Get user information
                var xhrUser = new XMLHttpRequest();
                xhrUser.responseType = 'json';
                xhrUser.open("POST", userEnpoint, true);
                xhrUser.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                xhrUser.setRequestHeader('Authorization', 'Bearer ' + response.access_token);
                xhrUser.send(new URLSearchParams({
                    client_id: clientId,
                    id_token: response.id_token
                }));

                xhrUser.onload = function () {
                    var response = xhrUser.response;

                    // Store user information
                    window.atLineUID = response.userId;
                    window.atDisplayName = response.displayName;

                    // Track user login event
                    web_event.track("line_oa", "login", {
                        items: [],
                        dims: {},
                        extra: {
                            name: response.displayName,
                            username: response.displayName,
                            line_uid: response.userId
                        }
                    });
                }

                document.getElementById("result").innerHTML = message;
            };

            xhr.responseType = 'json';
            xhr.open("POST", tokenEndpoint, true);
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            xhr.send(new URLSearchParams({
                client_id: clientId,
                client_secret: "<YOUR_CLIENT_SECRET>",
                code_verifier: window.sessionStorage.getItem("code_verifier"),
                grant_type: "authorization_code",
                redirect_uri: location.href.replace(location.search, ''),
                code: code
            }));
        }
    }
</script>

```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Replace `"<YOUR_CLIENT_SECRET>"` with your actual LINE Login channel's client ID.
{% endhint %}

### Example

By diligently following the Preparation, Initialization, and Token Exchange steps, you will seamlessly integrate LINE Login into your web application. Below is an example script demonstrating the entire process:

{% tabs %}
{% tab title="HTML" %}

```html
<script>
    const authorizeEndpoint = "https://access.line.me/oauth2/v2.1/authorize";
    const tokenEndpoint = "https://api.line.me/oauth2/v2.1/token";
    const userEnpoint = "https://api.line.me/v2/profile";
    const clientId = "<CLIENT_ID>";
    const clientSecrect = "<CLIENT_SECRECT>";

    if (window.location.search) {
        var args = new URLSearchParams(window.location.search);
        var code = args.get("code");

        if (code) {
            var xhr = new XMLHttpRequest();

            xhr.onload = function () {
                var response = xhr.response;
                var message;

                if (xhr.status == 200) {
                    message = "Access Token: " + JSON.stringify(response);
                }
                else {
                    message = "Error: " + response.error_description + " (" + response.error + ")";
                }

                // get info user
                var xhrUser = new XMLHttpRequest();
                xhrUser.responseType = 'json';
                xhrUser.open("POST", userEnpoint, true);
                xhrUser.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                xhrUser.setRequestHeader('Authorization', 'Bearer ' + response.access_token);
                xhrUser.send(new URLSearchParams({
                    client_id: clientId,
                    id_token: response.id_token
                }));

                xhrUser.onload = function () {
                    var response = xhrUser.response;

                    window.atLineUID = response.userId;
                    window.atDisplayName = response.displayName;

                    web_event.track("line_oa", "login", {
                        items: [],
                        dims: {},
                        extra: {
                            name: response.displayName,
                            username: response.displayName,
                            line_uid: response.userId
                        }
                    });
                }

                document.getElementById("result").innerHTML = message;
            };

            xhr.responseType = 'json';
            xhr.open("POST", tokenEndpoint, true);
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            xhr.send(new URLSearchParams({
                client_id: clientId,
                client_secret: clientSecret,
                code_verifier: window.sessionStorage.getItem("code_verifier"),
                grant_type: "authorization_code",
                redirect_uri: location.href.replace(location.search, ''),
                code: code
            }));
        }
    } 
    else {
        document.addEventListener("DOMContentLoaded", function (event) {
            var codeVerifier = generateRandomString(64);

            const challengeMethod = crypto.subtle ? "S256" : "plain";

            Promise.resolve()
                .then(() => {
                    if (challengeMethod === 'S256') {
                        return generateCodeChallenge(codeVerifier);
                    } 
                    else {
                        return codeVerifier;
                    }
                })
                .then(function (codeChallenge) {
                    window.sessionStorage.setItem("code_verifier", codeVerifier);

                    var redirectUri = window.location.href.split('?')[0];
                    var args = new URLSearchParams({
                        response_type: "code",
                        client_id: clientId,
                        code_challenge_method: challengeMethod,
                        code_challenge: codeChallenge,
                        redirect_uri: redirectUri,
                        state: "12345",
                        scope: "profile openid"
                    });
                    window.location = authorizeEndpoint + "/?" + args;
                });
        });
    }

    async function generateCodeChallenge(codeVerifier) {
        var digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(codeVerifier));

        return btoa(String.fromCharCode(...new Uint8Array(digest))).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
    }

    function generateRandomString(length) {
        var text = "";
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

        for (var i = 0; i < length; i++) {
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        }

        return text;
    }
</script>
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: 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:

```
GET https://docs.antsomi.com/developers-guide/3rd-party-integrations/line/integrating-line-login-with-your-web-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
