Skip to content

Commit

Permalink
Authorization fallback configuration and implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Dima Voytenko committed Feb 15, 2016
1 parent dc54118 commit a7b0d4a
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 7 deletions.
16 changes: 14 additions & 2 deletions examples/article-access.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
{
"authorization": "http://localhost:8002/amp-authorization.json?rid=READER_ID&url=CANONICAL_URL&_=RANDOM",
"pingback": "http://localhost:8002/amp-pingback?rid=READER_ID&url=CANONICAL_URL&views=AUTHDATA(views)",
"login": "http://localhost:8002/amp-login?rid=READER_ID&url=CANONICAL_URL"
"login": "http://localhost:8002/amp-login?rid=READER_ID&url=CANONICAL_URL",
"authorizationFallbackResponse": {"error": true}
}
</script>
<link href='https://fonts.googleapis.com/css?family=Georgia|Open+Sans|Roboto' rel='stylesheet' type='text/css'>
Expand Down Expand Up @@ -138,6 +139,13 @@
background: #ffa;
}

.error-section {
margin: 16px;
margin-top: 24px;
padding: 16px;
background: #fa7;
}

</style>
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
<script async custom-element="amp-access" src="https://cdn.ampproject.org/v0/amp-access-0.1.js"></script>
Expand Down Expand Up @@ -231,10 +239,14 @@ <h1 itemprop="headline">Lorem Ipsum</h1>
<a on="tap:meter-notif.hide">[Close]</a>
</section>

<section amp-access="NOT access" amp-access-hide class="login-section">
<section amp-access="NOT error AND NOT access" amp-access-hide class="login-section">
<a on="tap:amp-access.login">Login to read more!</a>
</section>

<section amp-access="error" amp-access-hide class="error-section">
Oops... Something broke.
</section>

<div amp-access="access" class="article-body" itemprop="articleBody">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Expand Down
16 changes: 14 additions & 2 deletions extensions/amp-access/0.1/amp-access.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ import {xhrFor} from '../../../src/xhr';
* type: !AccessType,
* authorization: (string|undefined),
* pingback: (string|undefined),
* login: (string|undefined)
* login: (string|undefined),
* authorizationFallbackResponse: !JSONObject
* }}
*/
let AccessConfigDef;
Expand Down Expand Up @@ -195,6 +196,8 @@ export class AccessService {
authorization: configJson['authorization'],
pingback: configJson['pingback'],
login: configJson['login'],
authorizationFallbackResponse:
configJson['authorizationFallbackResponse'],
};

// Check that all URLs are valid.
Expand Down Expand Up @@ -350,6 +353,16 @@ export class AccessService {
credentials: 'include',
requireAmpResponseSourceOrigin: true
}));
}).catch(error => {
this.analyticsEvent_('access-authorization-failed');
if (this.config_.authorizationFallbackResponse) {
// Use fallback.
setTimeout(() => {throw error;});
return this.config_.authorizationFallbackResponse;
} else {
// Rethrow the error.
throw error;
}
}).then(response => {
log.fine(TAG, 'Authorization response: ', response);
this.setAuthResponse_(response);
Expand All @@ -363,7 +376,6 @@ export class AccessService {
});
}).catch(error => {
log.error(TAG, 'Authorization failed: ', error);
this.analyticsEvent_('access-authorization-failed');
this.toggleTopClass_('amp-access-loading', false);
this.toggleTopClass_('amp-access-error', true);
});
Expand Down
44 changes: 43 additions & 1 deletion extensions/amp-access/0.1/test/test-amp-access.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,18 @@ describe('AccessService', () => {
expect(service.pubOrigin_).to.match(/^http.*/);
});

it('should initialize authorization fallback response', () => {
element.textContent = JSON.stringify({
'authorization': 'https://acme.com/a',
'pingback': 'https://acme.com/p',
'login': 'https://acme.com/l',
'authorizationFallbackResponse': {'error': true}
});
const service = new AccessService(window);
expect(service.config_.authorizationFallbackResponse).to.deep.equal(
{'error': true});
});

it('should NOT send events by default', () => {
element.textContent = JSON.stringify({
'authorization': 'https://acme.com/a',
Expand All @@ -226,7 +238,7 @@ describe('AccessService authorization', () => {

let sandbox;
let clock;
let configElement, elementOn, elementOff;
let configElement, elementOn, elementOff, elementError;
let xhrMock;
let cidMock;
let analyticsMock;
Expand Down Expand Up @@ -258,6 +270,11 @@ describe('AccessService authorization', () => {
elementOff.setAttribute('amp-access', 'NOT access');
document.body.appendChild(elementOff);

elementError = document.createElement('div');
elementError.setAttribute('amp-access', 'error');
elementError.setAttribute('amp-access-hide', '');
document.body.appendChild(elementError);

service = new AccessService(window);

sandbox.stub(service.resources_, 'mutateElement',
Expand Down Expand Up @@ -299,6 +316,9 @@ describe('AccessService authorization', () => {
if (elementOff.parentElement) {
elementOff.parentElement.removeChild(elementOff);
}
if (elementError.parentElement) {
elementError.parentElement.removeChild(elementError);
}
analyticsMock.verify();
sandbox.restore();
sandbox = null;
Expand Down Expand Up @@ -384,6 +404,28 @@ describe('AccessService authorization', () => {
});
});

it('should use fallback on authorization failure when available', () => {
expectGetReaderId('reader1');
xhrMock.expects('fetchJson')
.withExactArgs('https://acme.com/a?rid=reader1', {
credentials: 'include',
requireAmpResponseSourceOrigin: true
})
.returns(Promise.reject('intentional'))
.once();
service.config_.authorizationFallbackResponse = {'error': true};
const promise = service.runAuthorization_();
expect(document.documentElement).to.have.class('amp-access-loading');
expect(document.documentElement).not.to.have.class('amp-access-error');
return promise.then(() => {
expect(document.documentElement).not.to.have.class('amp-access-loading');
expect(document.documentElement).not.to.have.class('amp-access-error');
expect(elementOn).to.have.attribute('amp-access-hide');
expect(elementOff).not.to.have.attribute('amp-access-hide');
expect(elementError).not.to.have.attribute('amp-access-hide');
});
});

it('should resolve first-authorization promise after success', () => {
expectGetReaderId('reader1');
xhrMock.expects('fetchJson')
Expand Down
9 changes: 7 additions & 2 deletions extensions/amp-access/amp-access-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Property | Values | Description
authorization | &lt;URL&gt; | The HTTPS URL for the Authorization endpoint.
pingback | &lt;URL&gt; | The HTTPS URL for the Pingback endpoint.
login | &lt;URL&gt; | The HTTPS URL for the Login Page.
authorizationFallbackResponse | &lt;object&gt; | The JSON object to be used in place of the authorization response if it fails.
type | "client" or "server" | Default is “client”. The "server" option is under design discussion and these docs will be updated when it is ready.

*&lt;URL&gt;* values specify HTTPS URLs with substitution variables. The substitution variables are covered in more detail in the [Access URL Variables][7] section below.
Expand All @@ -117,7 +118,8 @@ Here’s an example of the AMP Access configuration:
"pingback":
"https://pub.com/amp-ping?rid={READER_ID}&url={SOURCE_URL}",
"login":
"https://pub.com/amp-login?rid={READER_ID}&url={SOURCE_URL}"
"https://pub.com/amp-login?rid={READER_ID}&url={SOURCE_URL}",
"authorizationFallbackResponse": {"error": true}
}
</script>
```
Expand Down Expand Up @@ -172,6 +174,8 @@ If Authorization request fails, `amp-access` expressions are not evaluated and w

We can extend the set of ```amp-access-*``` attributes as needed to support different obfuscation and rendering needs.

If Authorization request fails and the "authorizationFallbackResponse" response is not specified in the documentation, `amp-access` expressions are not evaluated and whether a section is visible or hidden is determined by the presence of the `amp-access-hide` attribute initially provided by the document.

Here’s an example that shows either login link or the complete content based on the subscription status:
```html
<header>
Expand Down Expand Up @@ -263,7 +267,7 @@ Authorization endpoint is called by AMP Runtime as a credentialed CORS endpoint.

AMP Runtime (or rather browser) observes cache response headers when calling Authorization endpoint. Thus the cached responses can be reused. It may or may not be desirable. If it is not desirable, the Publisher can user the appropriate cache control headers and/or RANDOM variable substitution for the endpoint URL.

If Authorization request fails the `amp-access` expressions will not be evaluated and whether a section is visible or hidden will be determined by the presence of the `amp-access-hide` attribute initially provided by the document.
If Authorization request fails, AMP Runtime will fallback to the "authorizationFallbackResponse", if it's specified in the configuration. In this case the authorization flow will proceed without as normal with the value of the "authorizationFallbackResponse" property in place of the authorization response. If the "authorizationFallbackResponse" is not specified, the authorization flow will fail, in which case the `amp-access` expressions will not be evaluated and whether a section is visible or hidden will be determined by the presence of the `amp-access-hide` attribute initially provided by the document.

Authorization request is automatically timed out and assumed to have failed after 3 seconds.

Expand Down Expand Up @@ -371,6 +375,7 @@ Both steps are covered by the AMP Access spec. The referrer can be injected into
- Feb 9: [First-click-free][13] and [Metering][12] sections.
- Feb 11: Nested field references such as `object.field` are now allowed.
- Feb 11: Authorization request timeout in [Authorization Endpoint][4].
- Feb 15: [Configuration][8] and [Authorization Endpoint][4] now allow "authorizationFallbackResponse" property that can be used when authorization fails.

#Appendix A: “amp-access” expression grammar

Expand Down

0 comments on commit a7b0d4a

Please sign in to comment.