Skip to content

Commit

Permalink
Add stateful OAuth flow
Browse files Browse the repository at this point in the history
  • Loading branch information
jinroh committed Dec 27, 2016
1 parent c567cfd commit 06253fc
Show file tree
Hide file tree
Showing 22 changed files with 954 additions and 546 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
"description": "Javascript library to interact with a cozy",
"main": "src/index.js",
"scripts": {
"build": "NODE_ENV=production webpack",
"build": "NODE_ENV=production NODE_TARGET=web webpack",
"watch": "NODE_ENV=production NODE_TARGET=web webpack --watch",
"lint": "standard './src/**/*.js' './test/**/*.js' webpack.config.js",
"test": "npm run test:unit && npm run test:v2 && npm run test:v3",
"test:unit": "NODE_ENV=test mocha-webpack 'test/unit/**.js'",
"test:v2": "NODE_ENV=test TARGET=http://localhost:9104 mocha-webpack 'test/integration/**.js'",
"test:v3": "NODE_ENV=test TARGET=http://localhost:8080 mocha-webpack 'test/integration/**.js'",
"test:unit": "NODE_ENV=test NODE_TARGET=node mocha-webpack 'test/unit/**.js'",
"test:v2": "NODE_ENV=test NODE_TARGET=node COZY_STACK_VERSION=2 COZY_STACK_URL=http://localhost:9104 mocha-webpack 'test/integration/**.js'",
"test:v3": "NODE_ENV=test NODE_TARGET=node COZY_STACK_VERSION=3 COZY_STACK_URL=http://localhost:8080 mocha-webpack 'test/integration/**.js'",
"clean": "rm -r dist && rm -r .tmp"
},
"repository": {
Expand Down Expand Up @@ -48,8 +49,7 @@
"standard": "8.6.0",
"standard-loader": "5.0.0",
"webpack": "1.13.3",
"webpack-node-externals": "1.5.4",
"whatwg-fetch": "1.0.0"
"webpack-node-externals": "1.5.4"
},
"/#######################################################": "",
"# this repo works as a cozy v2 client-side application #": "",
Expand Down
98 changes: 98 additions & 0 deletions src/auth_storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
export class LocalStorage {
constructor (prefix = 'cozy:oauth:', getLocalStorage = () => window.localStorage) {
// We try to deref the localstorage, and if it fails (because the user has
// deactivated its localstorage for instance) we fallback on a memory
// storage.
let storage
try {
storage = getLocalStorage()
} catch (e) {
storage = memStorage
}
this.storage = storage
this.prefix = prefix
}

save (key, value) {
return new Promise(resolve => resolve(
this.storage.setItem(this.prefix + key, JSON.stringify(value))))
}

load (key) {
return new Promise(resolve => {
const item = this.storage.getItem(this.prefix + key)
if (!item) {
resolve()
} else {
resolve(JSON.parse(item))
}
})
}

delete (key) {
return new Promise(resolve => resolve(
this.storage.removeItem(this.prefix + key)))
}

clear () {
return new Promise(resolve => {
const storage = this.storage
for (let i = 0; i < storage.length; i++) {
const key = storage.key(i)
if (key.indexOf(this.prefix) === 0) {
storage.removeItem(key)
}
}
resolve()
})
}
}

export class MemoryStorage {
constructor () {
this.hash = {}
}

save (key, value) {
return Promise.resolve(this.hash[key] = value)
}

load (key) {
return Promise.resolve(this.hash[key])
}

delete (key) {
return Promise.resolve(delete this.hash[key])
}

clear () {
return Promise.resolve(this.hash = {})
}
}

const memStorage = {
_h: {},
_k: [],
length: 0,
setItem (key, value) {
if (!this._h.hasOwnProperty(key)) {
this._k.push(key)
this.length++
}
this._h[key] = value
},
getItem (key) {
return this._h[key]
},
removeItem (key) {
if (delete this._h[key]) {
this._k.splice(this._k.indexOf(key), 1)
this.length--
return true
}
return false
},
key (index) {
return this._k[index]
}
}
54 changes: 54 additions & 0 deletions src/auth_v2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* global btoa */
const V2TOKEN_ABORT_TIMEOUT = 3000

export function getAccessToken () {
return new Promise(function (resolve, reject) {
if (typeof window === 'undefined') {
return reject(new Error('getV2Token should be used in browser'))
} else if (!window.parent) {
return reject(new Error('getV2Token should be used in iframe'))
} else if (!window.parent.postMessage) {
return reject(new Error('getV2Token should be used in modern browser'))
}
const origin = window.location.origin
const intent = {action: 'getToken'}
let timeout = null
const receiver = function (event) {
let token
try {
token = new AccessToken({
appName: event.data.appName,
token: event.data.token
})
} catch (e) {
reject(e)
return
}
window.removeEventListener('message', receiver)
clearTimeout(timeout)
resolve({ client: null, token })
}
window.addEventListener('message', receiver, false)
window.parent.postMessage(intent, origin)
timeout = setTimeout(() => {
reject(new Error('No response from parent iframe after 3s'))
}, V2TOKEN_ABORT_TIMEOUT)
})
}

export class AccessToken {
constructor (opts) {
this.appName = opts.appName || ''
this.token = opts.token || ''
if (this.appName === '') {
throw new Error('Missing appName parameter')
}
if (this.token === '') {
throw new Error('Missing token parameter')
}
}

toAuthHeader () {
return 'Basic ' + btoa(`${this.appName}:${this.token}`)
}
}
Loading

0 comments on commit 06253fc

Please sign in to comment.