> For the complete documentation index, see [llms.txt](https://docs.antsomi.com/developers-guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.antsomi.com/developers-guide/3rd-party-integrations/line/integrating-line-login-with-your-web-app.md).

# 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](/developers-guide/website/getting-started.md) 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 %}
