# LIFF Utilities [TOC] ###### tags: `LIFF` --- ```java import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.NoArgsConstructor; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.message.BasicNameValuePair; import tw.pawnshops.service.Servant; import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.Future; public class LineLoginUtils { private static final URI LINE_VERIFY_TOKEN_URL = URI.create("https://api.line.me/oauth2/v2.1/verify"); @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) private static class ErrorResponse { private String error; @JsonProperty("error_description") private String errorDescription; } /** * If the access token is valid, a 200 OK status code is returned with a * JSON object that has the following information. */ @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) private static class VerifyAccessTokenResponse { /** * Permissions granted to the access token. To learn more about * scopes, */ private String scope; /** * Channel ID for which the access token is issued */ @JsonProperty("client_id") private String clientId; /** * Number of seconds until the access token expires. */ @JsonProperty("expires_in") private Long expiresIn; } /** * The ID token payload is returned when the specified ID token is * successfully verified. */ @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) public static class VerifyIdTokenResponse { /** * URL used to generate the ID token. */ private String iss; /** * User ID for which the ID token was generated. */ private String sub; /** * Channel ID */ private String aud; /** * The expiry date of the ID token in UNIX time. */ private Long exp; /** * Time when the ID token was generated in UNIX time. */ private Long iat; /** * Time the user was authenticated in UNIX time. Not included if * the max_age value wasn't specified in the authorization * request. */ @JsonProperty("auth_time") private Long authTime; /** * The <code>nonce</code> value specified in the authorization * URL. Not included if the <code>nonce</code> value wasn't * specified in the authorization request. */ private String nonce; /** * A list of authentication methods used by the user. Not * included in the payload under certain conditions. * <p> * One or more of: * <ul> * <li><code>pwd</code>: Log in with email and password</li> * <li><code>lineautologin</code>: LINE automatic login * (including through LINE SDK)</li> * <li><code>lineqr</code>: Log in with QR code</li> * <li><code>linesso</code>: Log in with single sign-on</li> * </ul> * </p> */ private List<String> amr; /** * User's display name. Not included if the <code>profile</code> * scope wasn't specified in the authorization request. */ private String name; /** * User's profile image URL. Not included if the * <code>profile</code> scope wasn't specified in the * authorization request. */ private String picture; /** * User's email address. Not included if the <code>email</code> * scope wasn't specified in the authorization request. */ private String email; } /** * Verifies if an access token is valid. * * @param accessToken Access token * @return If the access token is valid, a 200 OK status code is * returned with a JSON object that has the following information. If * the access token has expired, a 400 Bad Request HTTP status code and * a JSON response are returned. */ public static Object verifyAccessToken(final String accessToken) { Object valueObject; try (CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault()) { httpClient.start(); HttpGet httpGet = new HttpGet( new URIBuilder(LINE_VERIFY_TOKEN_URL) .addParameter( "access_token", accessToken ) .build() ); Future<HttpResponse> future = httpClient.execute( httpGet, null ); HttpResponse httpResponse = future.get(); if (Objects.equals(200, httpResponse.getStatusLine().getStatusCode())) { valueObject = Servant.JSON_MAPPER.readValue( httpResponse.getEntity().getContent(), VerifyAccessTokenResponse.class ); } else { valueObject = Servant.JSON_MAPPER.readValue( httpResponse.getEntity().getContent(), ErrorResponse.class ); } httpClient.close(); } catch (Exception exception) { valueObject = new ErrorResponse(); } return valueObject; } /** * ID tokens are JSON web tokens (JWT) with information about the * user.It's possible for an attacker to spoof an ID token. Use this * call to verify that a received ID token is authentic, meaning you can * use it to obtain the user's profile information and email. * * @param idToken ID token * @param clientId Expected channel ID. * @return The ID token payload is returned when the specified ID token * is successfully verified; or a JSON object is returned when the * specified ID token fails to be verified. */ public static Object verifyIdToken(final String idToken, final String clientId) { Object valueObject; try (CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault()) { httpClient.start(); UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity( Arrays.asList( new BasicNameValuePair( "id_token", idToken ), new BasicNameValuePair( "client_id", clientId ) ) ); HttpPost httpPost = new HttpPost(LINE_VERIFY_TOKEN_URL); httpPost.addHeader( HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded" ); httpPost.setEntity(urlEncodedFormEntity); Future<HttpResponse> future = httpClient.execute( httpPost, null ); HttpResponse httpResponse = future.get(); if (Objects.equals(200, httpResponse.getStatusLine().getStatusCode())) { valueObject = Servant.JSON_MAPPER.readValue( httpResponse.getEntity().getContent(), VerifyIdTokenResponse.class ); } else { valueObject = Servant.JSON_MAPPER.readValue( httpResponse.getEntity().getContent(), ErrorResponse.class ); } httpClient.close(); } catch (Exception exception) { valueObject = new ErrorResponse(); } return valueObject; } } ```