Google AMP and amp-access over REST

Google’s AMP ( Accelerated Mobile Pages ) project has a feature which allows for hiding items on AMP pages which require access, typically for subscriptions or other content that is behind a login.

When diving into this, we found the examples online really only show how one would implement this if you were connecting to 3rd party systems for authentication – specifically OAuth using a Gmail account. While the example is interesting and functional, this does not apply to a large number of subscription systems which host and manage credentials and access levels internally.

Following is a really basic implementation of a REST type system which provides a simple authentication system using AMP.


index.html

This is the AMP page which will change what is displayed depending on what response amp-access receives when it calls the pingback or authorization url.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!doctype html>
<html amp>
        <head>

                <meta charset="utf-8">
                <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
                <link rel="canonical" href="https://www.onezeroless.com/google-amp-and-amp-access-over-rest/">

                <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>

                <script async src="https://cdn.ampproject.org/v0.js"></script>
                <script async custom-element="amp-access" src="https://cdn.ampproject.org/v0/amp-access-0.1.js"></script>
                <script async custom-element="amp-form" src="https://cdn.ampproject.org/v0/amp-form-0.1.js"></script>
                <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>

                <script id="amp-access" type="application/json">
                    {
                        "authorization": "https://onezeroless.com/amp/auth/?rid=READER_ID&url=CANONICAL_URL&ref=DOCUMENT_REFERRER&_=RANDOM",
                       "pingback": "https://onezeroless.com/amp/auth/?rid=READER_ID&url=CANONICAL_URL&ref=DOCUMENT_REFERRER&_=RANDOM",
                       "login": {
                         "sign-in": "https://onezeroless.com/amp/login/?rid=READER_ID&url=CANONICAL_URL",
                         "sign-out": "https://onezeroless.com/amp/logout"
                       },
                       "authorizationFallbackResponse": {
                           "access": false
                       }
                   }
               </script>

       </head>

       <body>

               <section amp-access="access" amp-access-hide>
                       <h2>Access Granted</h2>
                       <a on="tap:amp-access.login-sign-out">Logout</a>
               </section>

               <section amp-access="NOT access" amp-access-hide>
                       <h2>Permission Required</h2>
                       <a on="tap:amp-access.login-sign-in">Login</a>
               </section>

       </body>
</html>


auth/index.php

This script sets the AMP CORS response header, and responses to pingback and authorization requests, translating any session or cookie data set into valid boolean variables which AMP can understand. In this case, we read a value from a cookie which is only available when the user has authenticated. If that value is set, we response with the flag that signals to the above AMP html – that access is true. Otherwise, access is false and the Login link is displayed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
// set CORS and mime type
header('AMP-Access-Control-Allow-Source-Origin: https://onezeroless.com');
header('Content-type: application/json');

// set default 'access' response
$response = array(
        'access'=>false
);

// check if cookie was set for 'authorized'
$access = isset($_COOKIE['authorized']) ? $_COOKIE['authorized'] : false;

// if 'authorized', set 'access' in json response to 'true'
if($access) {
        $response = array_merge(
                $response,
                array(
                        'access'=>true
                )
        );
}

// return array for AMP
echo json_encode( $response );


login/index.php

This is the code for the login form. It does not need to be AMP so you can handle any custom authentication requirements here, including setting cookies or sessions and database lookup to validate credentials. In this case, it will set a site-wide cookie which expires in 1 hour if the username and password are both ‘test’, and then closes itself. The above AMP html will automatically call the pingback URL once it detects the login window has been closed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
$error = false;

if(isset($_POST['username'])) {

        // validate credentials
        if(($_POST['username'] == 'test') && ($_POST['password'] == 'test') ) {

                //login successful, set cookie
                setcookie('authorized', $_REQUEST['amp-access'], time()+3600,  "/", ".onezeroless.com");

                // close login window
                ?>
                <script type="text/javascript">
                        window.onload = function(e) {
                                window.close();
                        }
                </script>
                <?php
                exit();
        }
        $error = true;
}

// close login window
if(!isset($_REQUEST['rid'])) {
?>
        <script type="text/javascript">
                window.close();
        </script>
<?php
}

// display login error
echo ($error ? "<h2>Invalid Credentials</h2>" : "");
?>

<h3>Login Form</h3>

<form method="post" action="/amp/login/index.php">

        <label for="username">Username</label><br>
        <input type="text" value="test" name="username"><hr>

        <label for="password">Password</label><br>
        <input type="password" value="test" name="password"><hr>

        <input type="hidden" value="<?= $_REQUEST['url']; ?>" name="redirect">
        <input type="hidden" value="<?= $_REQUEST['rid']; ?>" name="ampid">

        <input type="submit" value="Login" />

</form>


logout/index.php

When a user logs out, it is typical to destroy any sessions or cookies. This code will destroy the cookie we set earlier, effectively invalidating the users access to the system. Like the login window, this will also self-close, and the above AMP html will then call the pingback URL once it detects this window has been closed to update it’s display status – once again, showing the Login link.

1
2
3
4
5
6
7
8
9
10
11
<?php
// destroy session
setcookie ("authorized", "", time() - 3600, "/", ".onezeroless.com");

// close logout window
?>
<script type="text/javascript">
        window.onload = function(e) {
                window.close();
        }
</script>

Click here for a Live Demo