diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..52e8547
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,274 @@
+{
+ "env": {
+ "browser": true,
+ "commonjs": true,
+ "es6": true
+ },
+ "extends": "eslint:recommended",
+ "rules": {
+ "accessor-pairs": "error",
+ "array-bracket-newline": "error",
+ "array-bracket-spacing": "error",
+ "array-callback-return": "error",
+ "array-element-newline": "error",
+ "arrow-body-style": "error",
+ "arrow-parens": "error",
+ "arrow-spacing": "error",
+ "block-scoped-var": "error",
+ "block-spacing": [
+ "error",
+ "never"
+ ],
+ "brace-style": "error",
+ "callback-return": "error",
+ "camelcase": "error",
+ "capitalized-comments": "off",
+ "class-methods-use-this": "error",
+ "comma-dangle": "error",
+ "comma-spacing": [
+ "error",
+ {
+ "after": true,
+ "before": false
+ }
+ ],
+ "comma-style": [
+ "error",
+ "last"
+ ],
+ "complexity": "error",
+ "computed-property-spacing": [
+ "error",
+ "never"
+ ],
+ "consistent-return": "off",
+ "consistent-this": "error",
+ "curly": "off",
+ "default-case": "error",
+ "dot-location": [
+ "error",
+ "property"
+ ],
+ "dot-notation": "off",
+ "eol-last": "error",
+ "eqeqeq": "off",
+ "for-direction": "error",
+ "func-call-spacing": "error",
+ "func-name-matching": "error",
+ "func-names": [
+ "error",
+ "never"
+ ],
+ "func-style": [
+ "error",
+ "declaration"
+ ],
+ "function-paren-newline": "error",
+ "generator-star-spacing": "error",
+ "getter-return": "error",
+ "global-require": "error",
+ "guard-for-in": "error",
+ "handle-callback-err": "error",
+ "id-blacklist": "error",
+ "id-length": "off",
+ "id-match": "error",
+ "implicit-arrow-linebreak": "error",
+ "indent": "off",
+ "indent-legacy": "off",
+ "init-declarations": "off",
+ "jsx-quotes": "error",
+ "key-spacing": "error",
+ "keyword-spacing": "off",
+ "line-comment-position": "off",
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "lines-around-comment": "error",
+ "lines-around-directive": "error",
+ "lines-between-class-members": "error",
+ "max-depth": "error",
+ "max-len": "off",
+ "max-lines": "error",
+ "max-nested-callbacks": "error",
+ "max-params": "error",
+ "max-statements": "error",
+ "max-statements-per-line": "error",
+ "multiline-comment-style": [
+ "error",
+ "separate-lines"
+ ],
+ "multiline-ternary": "error",
+ "new-parens": "error",
+ "newline-after-var": "off",
+ "newline-before-return": "off",
+ "newline-per-chained-call": "error",
+ "no-alert": "error",
+ "no-array-constructor": "error",
+ "no-await-in-loop": "error",
+ "no-bitwise": "error",
+ "no-buffer-constructor": "error",
+ "no-caller": "error",
+ "no-catch-shadow": "error",
+ "no-confusing-arrow": "error",
+ "no-continue": "error",
+ "no-console": ["error", { "allow": ["warn"] }],
+ "no-div-regex": "error",
+ "no-duplicate-imports": "error",
+ "no-else-return": "error",
+ "no-empty-function": "error",
+ "no-eq-null": "error",
+ "no-eval": "error",
+ "no-extend-native": "error",
+ "no-extra-bind": "error",
+ "no-extra-label": "error",
+ "no-extra-parens": "error",
+ "no-floating-decimal": "error",
+ "no-implicit-coercion": "error",
+ "no-implicit-globals": "error",
+ "no-implied-eval": "error",
+ "no-inline-comments": "off",
+ "no-inner-declarations": [
+ "error",
+ "functions"
+ ],
+ "no-invalid-this": "error",
+ "no-iterator": "error",
+ "no-label-var": "error",
+ "no-labels": "error",
+ "no-lone-blocks": "error",
+ "no-lonely-if": "error",
+ "no-loop-func": "error",
+ "no-magic-numbers": "off",
+ "no-mixed-operators": "error",
+ "no-mixed-requires": "error",
+ "no-multi-assign": "error",
+ "no-multi-spaces": "off",
+ "no-multi-str": "error",
+ "no-multiple-empty-lines": "off",
+ "no-native-reassign": "error",
+ "no-negated-condition": "error",
+ "no-negated-in-lhs": "error",
+ "no-nested-ternary": "error",
+ "no-new": "error",
+ "no-new-func": "error",
+ "no-new-object": "error",
+ "no-new-require": "error",
+ "no-new-wrappers": "error",
+ "no-octal-escape": "error",
+ "no-param-reassign": "off",
+ "no-path-concat": "error",
+ "no-plusplus": [
+ "error",
+ {
+ "allowForLoopAfterthoughts": true
+ }
+ ],
+ "no-process-env": "error",
+ "no-process-exit": "error",
+ "no-proto": "error",
+ "no-prototype-builtins": "off",
+ "no-restricted-globals": "error",
+ "no-restricted-imports": "error",
+ "no-restricted-modules": "error",
+ "no-restricted-properties": "error",
+ "no-restricted-syntax": "error",
+ "no-return-assign": "off",
+ "no-return-await": "error",
+ "no-script-url": "error",
+ "no-self-compare": "error",
+ "no-sequences": "off",
+ "no-shadow": "error",
+ "no-shadow-restricted-names": "error",
+ "no-spaced-func": "error",
+ "no-sync": "error",
+ "no-tabs": "error",
+ "no-template-curly-in-string": "error",
+ "no-ternary": "error",
+ "no-throw-literal": "error",
+ "no-trailing-spaces": "off",
+ "no-undef-init": "error",
+ "no-undefined": "error",
+ "no-underscore-dangle": "off",
+ "no-unmodified-loop-condition": "error",
+ "no-unneeded-ternary": "error",
+ "no-unused-expressions": "off",
+ "no-use-before-define": "error",
+ "no-useless-call": "error",
+ "no-useless-computed-key": "error",
+ "no-useless-concat": "error",
+ "no-useless-constructor": "error",
+ "no-useless-rename": "error",
+ "no-useless-return": "error",
+ "no-var": "off",
+ "no-void": "error",
+ "no-warning-comments": "error",
+ "no-whitespace-before-property": "error",
+ "no-with": "error",
+ "nonblock-statement-body-position": [
+ "error",
+ "any"
+ ],
+ "object-curly-newline": "error",
+ "object-curly-spacing": "error",
+ "object-property-newline": "error",
+ "object-shorthand": "error",
+ "one-var": "off",
+ "one-var-declaration-per-line": "error",
+ "operator-assignment": "error",
+ "operator-linebreak": "error",
+ "padded-blocks": "off",
+ "padding-line-between-statements": "error",
+ "prefer-arrow-callback": "off",
+ "prefer-const": "error",
+ "prefer-destructuring": "off",
+ "prefer-numeric-literals": "error",
+ "prefer-promise-reject-errors": "error",
+ "prefer-reflect": "off",
+ "prefer-rest-params": "error",
+ "prefer-spread": "error",
+ "prefer-template": "off",
+ "quote-props": "off",
+ "quotes": "off",
+ "radix": "error",
+ "require-await": "error",
+ "require-jsdoc": "off",
+ "rest-spread-spacing": "error",
+ "semi": "off",
+ "semi-spacing": "off",
+ "semi-style": [
+ "error",
+ "last"
+ ],
+ "sort-imports": "error",
+ "sort-keys": "off",
+ "sort-vars": "error",
+ "space-before-blocks": "off",
+ "space-before-function-paren": "off",
+ "space-in-parens": "off",
+ "space-infix-ops": "off",
+ "space-unary-ops": "error",
+ "spaced-comment": "off",
+ "strict": [
+ "error",
+ "never"
+ ],
+ "switch-colon-spacing": "error",
+ "symbol-description": "error",
+ "template-curly-spacing": "error",
+ "template-tag-spacing": "error",
+ "unicode-bom": [
+ "error",
+ "never"
+ ],
+ "valid-jsdoc": "error",
+ "vars-on-top": "off",
+ "wrap-iife": "error",
+ "wrap-regex": "error",
+ "yield-star-spacing": "error",
+ "yoda": [
+ "error",
+ "never"
+ ]
+ }
+}
diff --git a/.travis.yml b/.travis.yml
index a6d7edc..686f216 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,9 @@
language: node_js
node_js:
- '8.6.0'
+ - '9.11.1'
git:
depth: 3
-before_install:
- - 'sudo apt-get update && sudo apt-get install linux-image-generic '
after_success: 'npm run coveralls'
cache:
directiories:
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f144b56
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,4 @@
+## CHANGELOG
+
+Initial
+
diff --git a/mocha-test.html b/mocha-test.html
index 351143a..0005979 100644
--- a/mocha-test.html
+++ b/mocha-test.html
@@ -2,7 +2,7 @@
Mocha Tests
-
+
diff --git a/package.json b/package.json
index f43c4da..bb30dd9 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,8 @@
"scripts": {
"lint": "eslint src/AccessToken.js",
"instrument": "istanbul instrument src/AccessToken.js --output src/AccessToken_instrumented.js",
- "browserify": "./node_modules/browserify/bin/cmd.js test/accesstoken.js -o test/accesstoken_bundle.js",
- "mocha-headless": "mocha-headless-chrome -f mocha-test.html -c test/coverage.json",
+ "browserify": "./node_modules/browserify/bin/cmd.js test/accesstoken.js --exclude btoa -o test/accesstoken_bundle.js",
+ "mocha-headless": "mocha-headless-chrome -a no-sandbox -f mocha-test.html -c test/coverage.json",
"report": "istanbul report --root test/ lcov",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage",
"test": "npm run instrument && npm run browserify && npm run mocha-headless && npm run report"
@@ -15,15 +15,23 @@
"author": "github.com/gits2501",
"license": "MIT",
"dependencies": {
- "twiz-client-oauth": "file:../twiz-client-oauth",
- "twiz-client-redirect": "file:../twiz-client-redirect"
+ "twiz-client-oauth": "https://github.com/gits2501/twiz-client-oauth",
+ "twiz-client-redirect": "https://github.com/gits2501/twiz-client-redirect",
+ "twiz-client-utils": "https://github.com/gits2501/twiz-client-utils"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/gits2501/twiz-client-accesstoken.git"
+ },
+ "bugs": {
+ "url": "https://github.com/gits2501/twiz-client-accesstoken/issues"
},
"devDependencies": {
- "browserify": "^16.2.0",
- "coveralls": "^3.0.0",
+ "browserify": "^16.2.2",
+ "coveralls": "^3.0.2",
"eslint": "^4.19.1",
"istanbul": "^0.4.5",
- "mocha": "^5.1.1",
- "mocha-headless-chrome": "^2.0.0"
+ "mocha": "^5.2.0",
+ "mocha-headless-chrome": "^2.0.1"
}
}
diff --git a/src/AccessToken.js b/src/AccessToken.js
index 29ee030..97e990b 100644
--- a/src/AccessToken.js
+++ b/src/AccessToken.js
@@ -1,7 +1,8 @@
var OAuth = require('twiz-client-oauth');
var deliverData = require('twiz-client-redirect').prototype.deliverData;
- function AccessToken (){ // checks that oauth data is in redirection(callback) url, and makes sure
+ function AccessToken (){ // Prepares data for Access Token leg
+ // Checks that oauth data is in redirection(callback) url, and makes sure
// that oauth_token from url matches the one we saved in first step.
OAuth.call(this);
this.name = this.leg[2];
@@ -20,7 +21,9 @@ var deliverData = require('twiz-client-redirect').prototype.deliverData;
requestTokenNotSet: 'Request token was not set',
requestTokenNotSaved: 'Request token was not saved. Check that page url from which you make request match your redirection_url.',
noRepeat: "Cannot make another request with same redirection(callback) url",
- noStringProvided: "Expected string was not provided"
+ urlNotFound: "Current window location (url) not found",
+ noSessionData: 'Unable to find session data in current url',
+ spaWarning: 'Authorization data not found in url'
})
}
@@ -28,18 +31,16 @@ var deliverData = require('twiz-client-redirect').prototype.deliverData;
AccessToken.prototype.setAuthorizedTokens = function(){
- this.authorizeRedirectionUrl(),
- // set params for access token leg explicitly
- this.oauth[this.prefix + 'verifier'] = this.authorized.oauth_verifier // Put authorized verifier
- this.oauth[this.prefix + 'token'] = this.authorized.oauth_token; // Authorized token
+ this.parseRedirectionUrl(this.winLoc); // parse url
+ /* istanbul ignore else */
+ if(this.isAuthorizationDataInURL()){
+ this.authorize(this.redirectionData); // authorize token
+ // set params for access token leg explicitly
+ this.oauth[this.prefix + 'verifier'] = this.authorized.oauth_verifier // Put authorized verifier
+ this.oauth[this.prefix + 'token'] = this.authorized.oauth_token; // Authorized token
+ }
}
- AccessToken.prototype.authorizeRedirectionUrl = function(){// makes sure we have needed data in redirection url
- this.parseRedirectionUrl(this.winLoc); // parse
- return this.authorize(this.redirectionData); // authorize token
-
- }
-
AccessToken.prototype.parseRedirectionUrl = function(url){ // parses data in url
// console.log('in parseRedirectionUrl');
@@ -48,12 +49,12 @@ var deliverData = require('twiz-client-redirect').prototype.deliverData;
this.redirectionUrlParsed = true; // indicate that the url was already parsed
- // console.log(this.redirectionData.twiz_);
+ // console.log('redirectionData: >>', this.redirectionData);
}
AccessToken.prototype.parse = function(str, delimiter1, delimiter2){ // parses substring of a string (str)
- if(!str) throw this.CustomError('noStringProvided');
+ if(!str) throw this.CustomError('urlNotFound');
var start = str.search(delimiter1); // calculate from which index to take
var end ;
@@ -69,13 +70,11 @@ var deliverData = require('twiz-client-redirect').prototype.deliverData;
AccessToken.prototype.parseQueryParams = function (str){
var arr = [];
- if(!str) throw this.CustomError('noStringProvided');
-
if(str[0] === "?") str = str.substring(1); // remove "?" if we have one at beggining
arr = str.split('&') // make new array element on each "&"
- .map( function(el, i){
+ .map( function(el){
var arr2 = el.split("="); // for each element make new array element on each "="
return arr2;
@@ -101,14 +100,19 @@ var deliverData = require('twiz-client-redirect').prototype.deliverData;
return data;
}
-
+ //
+ AccessToken.prototype.isAuthorizationDataInURL = function(){ // check that we have valid twitter redirection url
+ if(!this.redirectionData.oauth_token && !this.redirectionData.oauth_verifier){ // not a redirection url
+ throw this.CustomError('spaWarning');
+ }
+ else return true
+ }
AccessToken.prototype.authorize = function(sent){ // check that sent data from redirection url has needed info
+ //console.log('in authorize');
if(this.isRequestTokenUsed(window.localStorage))
throw this.CustomError('noRepeat');
-
- // console.log('in authorize')
if(!sent.oauth_verifier) throw this.CustomError('verifierNotFound');
if(!sent.oauth_token) throw this.CustomError('tokenNotFound');
@@ -123,13 +127,13 @@ var deliverData = require('twiz-client-redirect').prototype.deliverData;
AccessToken.prototype.isRequestTokenUsed = function(storage){ // check that we have a token to use
if(storage.requestToken_ === "null") return true; // token whould be "null" only when loadRequestToken()
- // run twice on same redirection(callback) url
+ // runs twice on same redirection(callback) url
return false;
}
- AccessToken.prototype.loadRequestToken = function(storage, sent){
+ AccessToken.prototype.loadRequestToken = function(storage){
if(!storage.hasOwnProperty('requestToken_')) throw this.CustomError('requestTokenNotSaved');
@@ -141,33 +145,33 @@ var deliverData = require('twiz-client-redirect').prototype.deliverData;
storage.requestToken_ = null; // since we've loaded the token, mark it as
// used/erased with null
// console.log('after erasing storage.requestToken :', storage.requestToken_);
-
- if (!this.loadedRequestToken) throw this.CustomError('requestTokenNotSet');
+ // console.log('loadedRequestToken',this.loadedRequestToken);
+ if(!this.loadedRequestToken) throw this.CustomError('requestTokenNotSet');
}
AccessToken.prototype.getSessionData = function(){ // gets session data from redirection url
- console.log('in getSessionData')
- if(!this.redirectionUrlParsed);
- this.parseRedirectionUrl(window.location.href); // parse data from url
+ // console.log('in getSessionData')
+ if(!this.redirectionUrlParsed)
+ this.parseRedirectionUrl(window.location.href); // parse data from url
- if(!this.redirectionData.data){ // return if no session data
- console.log(this.messages.noSessionData);
- return;
- }
+ if(!this.redirectionData.data){ // return if no session data
+ console.warn(this.messages['noSessionData']);
+ return;
+ }
this.sessionData = this.parseSessionData(this.redirectionData.data) // further parsing of session data
- console.log(this.sessionData);
+ //console.log('sessionData: ',this.sessionData);
return this.sessionData;
}
AccessToken.prototype.parseSessionData = function(str){
- if(/%[0-9][0-9]/g.test(str)) // See if there are percent encoded chars
+ if(/%[0-9A-Z][0-9A-Z]/g.test(str)) // See if there are percent encoded chars
str = decodeURIComponent(decodeURIComponent(str)); // Decoding twice, since it was encoded twice
// (by OAuth 1.0a specification). See genSBS function.
return this.parseQueryParams(str); // Making an object from parsed key/values.
}
- AccessToken.prototype.deliverData = deliverData; // borrow function from Redirect module
+ AccessToken.prototype.deliverData = deliverData; // borrow function from Redirect module
module.exports = AccessToken;
diff --git a/test/accesstoken.js b/test/accesstoken.js
index 7039cd3..a790038 100644
--- a/test/accesstoken.js
+++ b/test/accesstoken.js
@@ -1,24 +1,138 @@
var AccessToken = require('../src/AccessToken_instrumented');
var assert = require('assert');
+function errorValidation(name, err){ // used to check thrown errors by name
+ if(err.name === name) return true;
+}
describe('Access Token', function(){
+ var at = new AccessToken(); // make instance
+
+ var session_data = '?data=quote%3DIf%2520one%2520way%2520be%2520better%2520than%2520another%252C%2520that%2520you%2520may%2520be%2520sure%2520is%2520natures%2520way.%2520%26author%3DAristotle';
+ var request_token = '&oauth_token=l4eELQAAAAAA0d0BAAABYxZJrAM';
+ var verifier = '&oauth_verifier=oWP8wRebbcfArwyV0oh4YxAWMeHUFrRC';
+ var query = session_data + request_token + verifier; // mock twitter redirection (callback) url with tokens
+
+ describe('Success', function(){
- describe('Access Token leg', function(){
- var request_token = 'longStringOfAlphanumerics33521' // mock request token from first leg (request token leg);
- var query = '?oauth_token='+request_token+'&oauth_verifier=similarStringOfAlphanumerics4224'; // make query string
- window.localStorage.requestToken_ = request_token // mock saved request token (in request token leg)
-
- var at = new AccessToken(); // make instance
- at.winLoc += query // mock authorized url (query string from twitter)
+
+
+ window.localStorage.requestToken_ = request_token.substring(13); // mock saved request token (in request
+ // token leg). Remove 'oauth_token='
+
it('ready ', function(){
- assert.doesNotThrow(at.setAuthorizedTokens.bind(at))
+ at.winLoc += query; // mock curent location with tokens from twitter
+ assert.doesNotThrow(at.setAuthorizedTokens.bind(at));
+ })
+
+ it('oauth_verifier from url parsed', function(){ // check that oauth verifier is parsed
+ assert.ok(at.redirectionData.oauth_token);
+ })
+
+ it('oauth_token from url parsed', function(){ // check that oauth token is parsed
+ assert.ok(at.redirectionData.oauth_verifier);
+ })
+
+ it('load (saved) request token',function(){ // make sure that saved token is loaded
+ assert.ok(at.loadedRequestToken);
+ })
+
+ it('mark request token as used', function(){ // check that loaded token is marked as used
+ var rToken = window.localStorage.requestToken_;
+ var used = rToken === 'null' ? false : rToken
+
+ assert.ok(!used);
})
+
+ describe('session data', function(){ // get session data
+ it('get session data', function(){
+ assert.ok(typeof at.getSessionData() === 'object');
+ })
+
+
+ it('redirection data', function(){ // check redirection data
+ var rdata = at.redirectionData.data;
+ assert.ok(typeof rdata === 'string' && rdata.length != 0);
+ })
+
+ it('session data', function(){
+ assert.ok(typeof at.sessionData === 'object')
+ })
+
+ })
})
- describe('not ready')
+ describe('Failure', function(){
+ var pageUrl = at.winLoc + query; // save current page
+
+ it('window location not found - throw error', function(){
+ at.winLoc = ''; // current url not present
+ assert.throws(at.setAuthorizedTokens.bind(at), errorValidation.bind(null, 'urlNotFound'))
+ })
+
+ it('request token already used - throw error', function(){ // request token is allready used
+ at.winLoc = pageUrl;
+ assert.throws(at.setAuthorizedTokens.bind(at), errorValidation(null, 'noRepeat'));
+ })
+
+ it('oauth_token missing - throw error', function(){
+ at.winLoc = session_data + verifier; // leave out oauth_token
+ window.localStorage.requestToken_ = request_token; // make token fresh
+ assert.throws(at.setAuthorizedTokens.bind(at), errorValidation.bind(null, 'tokenNotFound'))
+ })
+
+ it('oauth_verifier missing - throw error', function(){
+ at.winLoc = session_data + request_token; // leave out oauth_verifier
+ window.localStorage.requestToken_ = request_token; // make token fresh
+ assert.throws(at.setAuthorizedTokens.bind(at), errorValidation.bind(null, 'verifierNotFound'));
+ })
+
+ it('request token not saved - throw error', function(){
+ at.winLoc = session_data + request_token + verifier; // set current location (url)
+ delete window.localStorage.requestToken_ ; // make like token was not saved
+ assert.throws(at.setAuthorizedTokens.bind(at), errorValidation.bind(null, 'requestTokenNotSaved'));
+ })
+
+ it('token missmatch - throw error', function(){ // Check that received request_token
+ // is same as the one that is sent
+ at.winLoc = pageUrl + session_data + request_token + verifier; // Set current location (url)
+ window.localStorage.requestToken_ = 'NotSameAsTheOneReceived'; // Make saved request_token different
+ assert.throws(at.setAuthorizedTokens.bind(at), errorValidation.bind(null, 'tokenMissmatch'));
+ })
+
+ it('request token not set', function(){ // property is there but has no value
+ at.winLoc = session_data + request_token + verifier; // set current location (url)
+ window.localStorage.requestToken_ = ''; // make token fresh
+ assert.throws(at.setAuthorizedTokens.bind(at), errorValidation.bind(null, 'requestTokenNotSet'));
+ })
+
+
+
+
+ describe('session data', function(){
+
+ it('session data not found - log warning on console', function(){
+ at.winLoc = pageUrl + '?' + request_token.substring(1) + verifier; // leave out session data
+ window.localStorage.requestToken_ = request_token; // make token fresh
+ at.redirectionUrlParsed = false; // parse again
+ assert.doesNotThrow(at.getSessionData.bind(at), undefined);
+ })
+
+ })
+
+ describe('spa apps warning', function(){
+
+ it('Authorization data not found in url - throw error', function(){
+ at.winLoc = 'https://myApp.com/noQueryString'; // simulate no authorization data (request token
+ // and verifier)
+ assert.throws(at.setAuthorizedTokens.bind(at), errorValidation.bind(null, 'spaWarning'));
+ })
+
+ })
+ })
+
})