Starting with LIFF: Understanding liff.init

LIFF is a powerful tool for integrating web apps seamlessly with LINE, unlocking native features in your web apps. It's initialized via the crucial liff.init call. 

Recently I ran into some problems with liff.init, requiring me to take a closer look at exactly what it does. Therefore, I thought I would share here. This blog aims to: 

- Instruct on how to get a web app up and running with LIFF. 
- Take a closer look at what happens "under the hood" when we call liff.init. 
- Discuss retaining the chat_message.write scope between LIFF apps. 
- Recognizing the LIFF browser and external browser in line. 

## Getting up and running

When starting to developing apps for the LIFF browser, newcomers can get set up soon enough. You can do it yourself roughly like this:  

1. Log into LINE Developers
2. Create a line login channel 
3. Create an LIFF app within that channel 
4. Set an endpoint URL (❖1)
4. Use LIFF app's LIFF ID with liff.init in your source code (❖2)
5. Test by opening LIFF URL from a line talk room -> `https://liff.line.me/` + `liffId`!


**(❖1)**
Step 4 requires that you have somewhere to host your app. The address needs to be https. While developing or testing, I recommend using something like NGROK to forward your localhost. 

**(❖2)**
Step 5 will look something like this: 
```javascript
const liffId = "my-liff-id",;
liff.init({ 
  liffId,
});
```

I usually run this code in inside `main.ts`, before creating my Vue app. If using another framework like react you would probably do something similar in `index.tsx`, or `App.ts`. As we will see in the next part, the timing of the call is important.

It starts an asynchronous process that involves communication with LINE's servers, to authenticate the user and obtain necessary permissions. I started using this functionality before I understood what it does. This of course lead to problems I wasn't able to identify. If you want to avoid that experience, I recommend reading on. 

## So what actually happens when we call liff.init ? 

Now that you have a web app calling liff.init with a valid LIFF Id, we can look liff.init more closely. We're mainly interested in it's asynchronous authentication process. The screenshot below is from a v2.11.0 [release note](https://developers.line.biz/en/docs/liff/release-notes/#redirect-example). It is not the most recent (2021), but I like how it visually represents the steps involved. The process is similarly described in the up-to-date [Documentation](https://developers.line.biz/en/docs/liff/opening-liff-app/#redirect-flow).

![RedirectExampleEN.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/c5442cf2-20d7-0747-0b3a-18f7b1c0c595.png)


Let's go over the information step by step.

1. **The user accesses the LIFF URL, sending a request to the LIFF server provided by LINE.**
2. **Upon receiving the user's request, the LIFF servers redirect the user to the "Primary redirect URL"** 
This "Primary redirect URL" is the endpoint URL you specified in the LINE Developers console just before. Any path behind the LIFF URL As detailed in the image, the primary redirect url includes a URL fragment containing credential information in the form of: access_token, context_token, feature_token and id_token. 

3. **The app then executes the liff.init method with these tokens.** This involves checking the authenticity of the tokens and user trying to access the LIFF app. This drops the URL fragment tokens from the URL. Only the domain and a liff.state parameter containing the path are left. Note that specifying a path like "/my-page-path" is optional.

4. **Finally, the users is redirected to the "Secondary redirect URL".** This is still the endpoint URL we specified, but with the path information from the parameter returned as the URL path. 

Below you can see a noteworthy warning from the LINE Developers documentation. It indicicates the importance of the timing of the liff.init call. More specifically, it warns against any redirection or built in router functionality during liff.init. Having understood the asynchronous athorization process, we can begin to understand why that is.

![NoteOnInitializing.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/3f0721eb-6861-7c31-a042-123ebe08ff5b.png)

## On the chat_message.write scope

The chat_message.write scope provides functionality to your LIFF app. It enables the liff.sendMessages method. This is one of the quickest ways to send messages from a LIFF app, and requires relatively little　setup and handling of user data. However, it is limited to sending messages to the talk room the LIFF app was opened from. 

To use it, first enable the chat_message.write scope in your LIFF app settings, in your channel, in the LINE Developers console. This will allow you to use liff.sendMessages from your LIFF app.

Say however, that you or your client want to open your LIFF app not directly from the talk room. 

We recently ran into this situation. Per client request, a middle app was opened from the talk room, and that app linked to our LIFF app. The desired behavior was that chat_message.write would still be available in our app.

This behavior is not impossible to achieve. However, as we found out, this works only if both apps are LIFF apps. The second app, which you transition from, also needs to be opened through the LIFF URL and not the endpoint URL. This is explained very clearly in a [section the LINE Developers docs](https://developers.line.biz/en/docs/liff/opening-liff-app/#about-chat-message-write-scope).

In our case, our LIFF app was opened correctly through the LIFF URL. However, the middle app was not a LIFF app, meaning that the scope did not persist. Because of that, calling liff.sendMessages did not work in our app. Initially, our error handling led people to think the problem was also on our end. However, we quickly noticed that the middle app was at fault. We were able to tell, because it is quite easy to tell a LIFF app from a regular web app. All you have to do is check which browser (within LINE) is used to open the app.

## Recognizing the LIFF browser 

Finally, let me share how to recognize whether you are correctly using a LIFF app in the LIFF browser. There are subtle visual differences. Below are screenshots, from my screen after  opening the same app, through the LIFF URL on the left, and directly through the origin URL endpoint on the right. 

As seen in the image, the top section of the page has subtle differences. However, the navigation bar at the bottom is the easiest way to spot a non-LIFF browser opened in LINE. As of April 2024, this is still true for both Android and iPhone devices. 

![Screenshot 2024-04-30 at 13.56.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/7277846b-94fb-a944-76ab-9af9ca2c68a0.png)