Web API Authorization Guide¶
In this guide we explore how to obtain a valid OAuth access token in order to send HTTP requests to the Crowdynews Web APIs.
This guide contains the following topics:
Roles¶
This guide uses the following vocabulary to describe roles:
ROLE | ROLE DESCRIPTION |
---|---|
Consumer | The entity on behalf of whom the HTTP requests are made. For example, a user or machine. |
Client | The entity making the HTTP requests. For example, a JavaScript Web Application or Web API. |
Supported Authorization Flows¶
The Crowdynews Web APIs can either be consumed by users or machines:
-
Users
For example, your client is a JavaScript Web Application where a user can moderate a content queue via the Content API.
-
Machines
For example, your client is a Web API that creates collections via the Control API.
Depending on if HTTP requests are made on behalf of users or machines, the access token sent with a request will be obtained by either using the Implicit Grant Flow (user) or the Client Credentials Grant Flow (machine).
Implicit Grant Flow¶
This flow is for clients that are implemented entirely using JavaScript and are running in the user’s browser. You don't need any server-side code to use it. It must be used by clients that want to make requests to the Crowdynews Web APIs on behalf of a user.
The most notable difference compared to other authorization flows is that there is no refresh token provided. The reason being that the client can't guarantee to keep its client credentials a secret. Therefore only the client identifier is required to obtain an access token. Also note that the user and client must both be registered with the Crowdynews Authorization API. After doing this you'll receive your unique client identifier and users will be able to gain access to your client. You can register by contacting [email protected].
This flow is implemented according to RFC-6749.
1. Your client requests authorization¶
Make a request to:
GET https://auth.crowdynews.com/v1/authorize
The following request query parameters are required:
QUERY PARAMETER | VALUE DESCRIPTION |
---|---|
response_type | Must be set to token . |
client_id | The client identifier provided to you by Crowdynews after registering your client. |
redirect_uri | The URI to redirect to after the user grants or denies permission. This URI needs to be provided to Crowdynews when you register your client. This URI usually points to where the authorization request originated from. |
For example:
GET https://auth.crowdynews.com/v1/authorize?response_type=token&client_id=9173409a5f69fa5f8d169b1000cf269f&redirect_uri=https://my-app.com/callback
2. The user is asked to authorize access¶
If the user is not logged in, they are prompted to do so using their Crowdynews username and password. Then the Authorization API will present details of what user information will be accessed by the client by presenting a confirmation dialog.
3. The user is redirected back to your specified redirect URI¶
After the user grants or denies the client access, the
Authorization API will redirect the user to the URI that
was specified as the redirect_uri
query paramater in
step 1. For
our example, this would be the address:
https://my-app.com/callback
If the user has granted access, the final URL, i.e. the redirect URI, will contain a hash fragment with the following data encoded as a query string:
FRAGMENT | VALUE DESCRIPTION |
---|---|
access_token | An access token that can be provided in subsequent calls to the Crowdynews Web APIs. |
token_type | The type of access token that was issued. Will always be set to Bearer . |
For example:
https://my-app.com/callback#access_token=d0944f4ea3ee303740b4dfb400c8eb3ff833ebfc98d7829534c17ea42cdaecbd333e0062204c007c993b1c3768c4be2b70120e70c353ed83c9ef7f06000c4bef&token_type=Bearer
4. Store and use the obtained access token¶
The client can now extract the access token from the hash fragment described in step 3 and store it using, for example, the web browser Local Storage API. See the reference implementation for an approach. The client can now make authenticated requests.
Note that access tokens are deliberately set to expire after a short time. When it does, the consumer needs to go through the entire implicit grant flow to obtain a new and valid access token.
Client Credentials Grant Flow¶
This flow is for clients that are implemented on the server-side. It must be used by clients that want to make requests to the Crowdynews Web APIs on behalf of themselves. This means the client is also the consumer.
The most notable difference compared to other authorization flows is that there's no authorization included, because there is no user involved. Also note that the client must be registered with the Crowdynews Authorization API. After doing this you'll receive your unique client identifier and client secret which can be used by your client. You can register by contacting [email protected].
This flow is implemented according to RFC-6749.
1. Your client requests tokens¶
Make a request to:
POST https://auth.crowdynews.com/v1/oauth/token
The following request headers are required:
HEADER FIELD | VALUE DESCRIPTION |
---|---|
Content-Type | The content type of the request body. This field must be set to application/json . |
Authorization* | Base 64 encoded string that contains the client identifier and client secret provided to you by Crowdynews after registering your client. This field must have the format: Authorization: Basic <base64 encoded clientId:clientSecret> . |
* See the reference implementation for an approach on how to construct this header.
The following request body parameters are required:
REQUEST BODY DATA | VALUE TYPE | VALUE DESCRIPTION |
---|---|---|
grant_type | String | Must be set to client_credentials . |
Example request:
Content-Type: application/json
Authorization: Basic ODcwNDQxOWQ1ZTYxY2Q1NmU1NzRiNjJmMjk4MmRhMzY6OWI5MTZiYzJiNGJhOTY5ZjM4MWEyYzkyMzVmN2IyNWI1ODJmOTFhMGY0ODRiNTJhZDZlMzE5OWY5NWE4MDk5OQ==
POST https://auth.crowdynews.com/v1/oauth/token
{
"grant_type": "client_credentials"
}
On success, the HTTP status code in the response header will
be 200 OK
and the response body will contain the tokens
in JSON format. On error, the header status code will be an
error code and the response body will contain an
error object.
RESPONSE BODY DATA | VALUE TYPE | VALUE DESCRIPTION |
---|---|---|
access_token | String | The token to use to make authenticated requests. |
refresh_token | String | The token to use to obtain a new access token when one expired. |
token_type | String | The type of the issued tokens. Will always be set to Bearer . |
Example response:
HTTP/1.1 200
{
"access_token": "e0933f4ea3ee303740b4dfb400c8eb3ff833ebfc98d7829534c17ea42cdaecbdfffe0062204c007c663b1c3768c4be2b71130ef0c353ed83c9ef7f06040c4ddf",
"refresh_token": "78f3e68d303ffa626a766692b508be5c3c3b8a6156110fdca7c60b65ece2c144132f2f8f3c3743eef6f94fed26f3bfb303461a854b73adb0785e2f664f53e888",
"token_type": "Bearer"
}
2. Store and use the obtained access token¶
The client can now extract the access token from the response body described in step 1 and store it in, for example, a database. See the reference implementation for an approach. The client can now make authenticated requests.
3. Refresh the access token after expiration¶
Access tokens are deliberately set to expire after a short time, after which new tokens may be granted by supplying the refresh token that was obtained during the client credentials exchange as described in step 1.
Make a request to:
POST https://auth.crowdynews.com/v1/oauth/token
The following request headers are required:
HEADER FIELD | VALUE DESCRIPTION |
---|---|
Content-Type | The content type of the request body. This field must be set to application/json . |
Authorization* | Base 64 encoded string that contains the client identifier and client secret provided to you by Crowdynews after registering your client. This field must have the format: Authorization: Basic <base64 encoded clientId:clientSecret> . |
* See the reference implementation for an approach on how to construct this header.
The following request body parameters are required:
REQUEST BODY DATA | VALUE TYPE | VALUE DESCRIPTION |
---|---|---|
grant_type | String | Must be set to refresh_token . |
refresh_token* | String | The token used to refresh the access token. |
* The refresh token has been obtained in step 1.
Example request:
Content-Type: application/json
Authorization: Basic ODcwNDQxOWQ1ZTYxY2Q1NmU1NzRiNjJmMjk4MmRhMzY6OWI5MTZiYzJiNGJhOTY5ZjM4MWEyYzkyMzVmN2IyNWI1ODJmOTFhMGY0ODRiNTJhZDZlMzE5OWY5NWE4MDk5OQ==
POST https://auth.crowdynews.com/v1/oauth/token
{
"grant_type": "refresh_token",
"refresh_token": "78f3e68d303ffa626a766692b508be5c3c3b8a6156110fdca7c60b65ece2c144132f2f8f3c3743eef6f94fed26f3bfb303461a854b73adb0785e2f664f53e888"
}
On success, the HTTP status code in the response header will
be 200 OK
and the response body will contain the refreshed
access- and refresh tokens in JSON format. On error, the
header status code will be an error code and the
response body will contain an error object.
RESPONSE BODY DATA | VALUE TYPE | VALUE DESCRIPTION |
---|---|---|
access_token | Boolean | The token to use to make authenticated requests. |
refresh_token | Boolean | The token to use to obtain a new access token when one expired. |
token_type | String | The type of the issued tokens. Will always be set to Bearer . |
Example response:
HTTP/1.1 200
{
"access_token": "b3795396b638f3ef1658c409f474c280131663b8ca40b81f20f29659bc703fe14fcefd739a2f1a1e858bae32e819fad8e47ee1f1a1634493a0c544009c26d6ec",
"refresh_token": "99a99f9f0f3b529ffa0f8efc5e04d1dc0492133f19d474fc26ff688b2bc6127344ee639a0fccf1ac748947a29a7bdc4b1a5648e4da27e6639656612895c50aee",
"token_type": "Bearer"
}
Requesting Access Token Information¶
After an access token has been obtained, by means of any authorization flow whatsoever, said access token can be used to request information from the Authorization API.
Expiration Details¶
In order to get the expiration details of an access token, make a request to:
GET https://auth.crowdynews.com/v1/oauth/token/info
The following request query parameters are required:
QUERY PARAMETER | VALUE DESCRIPTION |
---|---|
access_token | The access token of which expiration information is requested. |
Example request:
GET https://auth.crowdynews.com/v1/oauth/token/info?access_token=cf765396b638f3ef1668c409f474c280131663b8ca40b81f20f29659bc703fe14fcefd739a2f1a1e858bae32e819fad8e47ee1f1a1674493a0c544009c26d6ef
On success, the HTTP status code in the response header is
200 OK
and the response body will contain the access token
expiration details in JSON format. On error, the header
status code is an error code and the
response body will contain an error object.
RESPONSE BODY DATA | VALUE TYPE | VALUE DESCRIPTION |
---|---|---|
active | Boolean | If the access token is active or not. |
expired | Boolean | If the access token has expired or not. |
expires | String | The timestamp denoting when the access token will expire. |
ttl | Number | The access token time to live in milliseconds. This property is 0 when an access token has expired. |
Example response:
HTTP/1.1 200
{
"active": true,
"expired": false,
"expires": "2016-06-14T11:27:45.568Z",
"ttl": 3577240
}
Consumer Details¶
In order to get details about the consumer associated with an access token, make a request to:
GET https://auth.crowdynews.com/v1/oauth/me
The following request query parameters are required:
QUERY PARAMETER | VALUE DESCRIPTION |
---|---|
access_token | The access token of which consumer information is requested. |
Example request:
GET https://auth.crowdynews.com/v1/oauth/me?access_token=b3795396b638f3ef1658c409f474c280131663b8ca40b81f20f29659bc703fe14fcefd739a2f1a1e858bae32e819fad8e47ee1f1a1634493a0c544009c26d6ec
On success, the HTTP status code in the response header is
200 OK
and the response body will contain the consumer
details in JSON format. On error, the header status code
is an error code and the
response body will contain an error object.
Note that the returned details will differ depending on which authorization flow was used to obtain the access token, i.e. if it was obtained on behalf of a user or machine.
RESPONSE BODY DATA | VALUE TYPE | VALUE DESCRIPTION |
---|---|---|
privileges | Array | Contains the consumer's access privileges. Each element is a string denoting a privilege. |
consumer_id | String | A hexadecimal string to uniquely identify the consumer. |
consumer_name | String | A human readable consumer identifier. |
consumer_type | String | The type of consumer. Will either be user or client (machine). |
consumer_email | String | A unique email address associated with the consumer. |
language | String | Optional. A valid ISO 639-1 language code. A language will only be returned when the consumer is a user . |
Example response:
HTTP/1.1 200
{
"privileges": [ ],
"consumer_id": "9fd4d467d438e429f48e416e2f361d31",
"consumer_name": "Mr. Robot",
"consumer_type": "client",
"consumer_email": "[email protected]"
}
In case the access token is associated with a user, an example response might be:
HTTP/1.1 200
{
"privileges": [
"MY_ACCOUNT"
],
"consumer_id": "569f34286f24a9f969499adf",
"consumer_name": "John Doe",
"consumer_type": "user",
"consumer_email": "[email protected]",
"language": "en"
}
Reference implementations¶
The following example illustrates an approach to implement the above described authorization flows in JavaScript.
Implicit Grant reference¶
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Implicit Grant</title>
</head>
<body>
<section>
<p>Open your console.</p>
</section>
<script type="text/javascript">
'use strict';
const AUTHORIZATION_API = 'https://auth.crowdynews.com/v1/oauth';
const CLIENT_ID = '9173409a5f69fa5f8d169b1000cf269f';
const REDIRECT_URI = 'https://my-app.com';
function getAccessToken() {
return localStorage.getItem('accessToken');
}
function getUserDetails() {
const user = localStorage.getItem('user');
try {
return JSON.parse(user);
} catch (err) {
console.log('Parse Error: user details is invalid JSON');
}
}
function extractTokenFromUrl() {
const match = /access_token=([a-z0-9]+)/.exec(window.location.hash);
const accessToken = match ? match[1] : undefined;
if (accessToken) {
window.location.hash = '';
}
return accessToken;
}
function authorizeRedirect() {
const url = `${AUTHORIZATION_API}/authorize?response_type=token&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}`;
window.location.replace(url);
}
function validateToken(token, next) {
const url = `${AUTHORIZATION_API}/me?access_token=${token}`;
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
const xhrIsDone = xhr.readyState === 4;
const statusIsOk = xhr.status === 200;
const tokenIsInvalid = xhr.status === 401;
if (xhrIsDone && !statusIsOk) {
return next(
new Error(xhr.responseText)
);
}
if (xhrIsDone && tokenIsInvalid) {
return authorizeRedirect();
}
if (xhrIsDone && statusIsOk) {
return next(null, xhr.responseText);
}
};
xhr.open('GET', url, true);
xhr.send();
}
function start(done) {
const token = extractTokenFromUrl() || getAccessToken();
if (!token) {
return authorizeRedirect();
}
validateToken(token, (err, userDetails) => {
if (err) {
return done(err);
}
localStorage.setItem('user', userDetails);
localStorage.setItem('accessToken', token);
return done();
});
}
start(err => {
if (err) {
console.log(`Error: ${err}`);
} else {
const userDetails = getUserDetails();
const accessToken = getAccessToken();
console.log(`user ${userDetails.consumer_email} has successfully logged in`);
console.log(`with access token ${accessToken}`);
}
});
</script>
</body>
</html>
Client Credentials Grant reference¶
'use strict';
const request = require('request');
const AUTHORIZATION_API = 'https://auth.crowdynews.com/v1/oauth/token';
const CLIENT_ID = '8173f09a5f69ff5f8d169b1333cf264e';
const CLIENT_SECRET = 'ff813bc2b4ba9691381a2c923597b25b582e91a05494b52ad6e3199f95a80255';
function createAuthorizationHeaderValue(clientId, clientSecret) {
const encoded = `${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`;
const buffer = new Buffer(encoded).toString('base64');
return `Basic ${buffer}`;
}
request.post({
url: AUTHORIZATION_API,
json: true,
headers: {
Authorization: createAuthorizationHeaderValue(CLIENT_ID, CLIENT_SECRET)
},
body: {
grant_type: 'client_credentials'
}
}, (err, res, body) => {
if (err) {
console.log(`error: ${err}`);
}
console.log(`access token: ${body.access_token}`);
console.log(`refresh token: ${body.refresh_token}`);
});