import ifPromise from 'onyx-common/ifPromise'
import isPromise from 'onyx-common/isPromise'
import hasKey from 'onyx-common/hasKey'
/*
All API calls are broken into (at least) 2 methods:
- A "public" method the app-land code we call from the store. (ie: login)
- A "private" method by the same name but with a "_" in front that actually handles the raw API call (ie: _login)
Why?
- We can test the API contract with the private method.
- With the public, we can manipulate the raw result we get back to make it easier to work with in our app itself
- ie: we camelize the field names, filter down the fields we care about, etc.
*/
const ZEventsApiAuth = ({ prototype }) => {
prototype.normalizeLoginableReturn = function (data) {
const ret = {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in,
createdAt: data.created_at,
isAdmin: data.status === 'admin'
}
if (hasKey(data, 'user_id')) ret.id = data.user_id
return ret
}
/**
* @typedef {object} ManualLoginReturn
* @property {string} accessToken - access token
* @property {string} refreshToken - refresh token
*/
/**
* @function manualLogin
* @param {object} payload - The manualLogin payload
* @param {string} payload.accessToken - current accessToken
* @param {string} payload.refreshToken - current refreshToken
* @returns {ManualLoginReturn} - manualLogin return
* @example
*
* manualLogin({
* accessToken: 'asdfasdfsdfadsf',
* refreshToken: 'asdfasdfasdf'
* })
*/
prototype.manualLogin = function (payload) {
const finalPayload = {
refreshToken: payload.refreshToken,
accessToken: payload.accessToken
}
return this.authable.onLogin(finalPayload)
}
/**
* @function SSOLogin
* @param {object} payload - The SSOLogin payload
* @param {string} payload.accessToken - current accessToken
* @param {string} payload.refreshToken - current refreshToken
* @returns {ManualLoginReturn} - SSOLogin return
* @example
*
* SSOLogin({
* accessToken: 'asdfasdfsdfadsf',
* refreshToken: 'asdfasdfasdf'
* })
*/
prototype.SSOLogin = prototype.manualLogin
prototype._loginWithFirebase = function (sentIn) {
const payload = {
url: this.getUrl('/api/v1/authentication/firebase'),
method: 'POST',
requestType: 'json',
data: {
...sentIn,
grant_type: 'refresh_token',
...this.getAuthParams()
}
}
return this.fetchWrap(payload)
}
/**
* @typedef {object} FirebaseLoginReturn
* @property {string} accessToken - access token
* @property {string} refreshToken - refresh token
* @property {string} expiresIn - expires in
* @property {string} createdAt - created at
* @property {string} isAdmin - whether or not user is admin
*/
/**
* @function loginWithFirebase
* @param {object} payload - The loginWithFirebase payload
* @param {string} payload.firebaseToken - user email
* @returns {FirebaseLoginReturn} - login return
* @example
*
* loginWithFirebase({
* firebaseToken: 'xxxxxxxxxxxxxxx'
* })
*/
prototype.loginWithFirebase = function (payload) {
// this gives us the ability to pass in the response from _login directly if we want to
const finalPayload = {
firebase_token: payload.firebaseToken
}
const raw = ifPromise(payload, () => this._loginWithFirebase(finalPayload))
return raw
.then(res => this.normalizeLoginableReturn(res.data))
.then(data => this.authable.onLogin(data))
.catch(res => this.onError('loginWithFirebase', res))
}
prototype._login = function (sentIn) {
const {
email,
password
} = sentIn
const payload = {
url: this.getUrl('/api/v1/oauth/token'),
method: 'POST',
requestType: 'json',
data: {
grant_type: 'password',
email,
password,
...this.getAuthParams()
}
}
return this.fetchWrap(payload)
}
/**
* @typedef {object} LoginReturn
* @property {number} id - user id
* @property {string} accessToken - access token
* @property {string} refreshToken - refresh token
* @property {number} expiresIn - seconds until accessToken is expired
* @property {number} createdAt - access token create timestamp
* @property {string} isAdmin - whether or not user is admin
*/
/**
* @function login
* @param {object} payload - The login payload
* @param {string} payload.email - email address of user
* @param {string} payload.password - password to test against
* @returns {LoginReturn} - LoginReturn return
* @example
*
* login({
* email: 'b@.com',
* password: 'bah'
* })
*/
prototype.login = function (payload) {
// this gives us the ability to pass in the response from _login directly if we want to
const finalPayload = {
email: payload.email,
password: payload.password
}
const raw = ifPromise(payload, () => this._login(finalPayload))
return raw
.then(res => this.normalizeLoginableReturn(res.data))
.then(data => this.authable.onLogin(data))
.catch(res => {
const mapError = {
'Invalid Email Adress or password.': 'LOGIN_INVALID'
}
return this.onError('login', res, mapError)
})
}
prototype._logout = function (sentIn = {}) {
const {
token
} = sentIn
const url = this.getUrl('/api/v1/oauth/revoke')
const payload = {
url,
method: 'POST',
requestType: 'json',
data: {
token,
...this.getAuthParams()
}
}
return this.fetchWrap(payload)
}
/**
* @function logout
* @param {object} [payload] - The logout payload
* @param {string} [payload.accessToken=this.authable.getAccessToken()] - accessToken to process
* @returns {void}
* @example
*
* logout({
* accessToken: 'asdfasdfasdfadsfd'
* })
*/
prototype.logout = function (payload = {}) {
this.authable.onLogout()
let raw = payload
if (!isPromise(raw)) {
const {
accessToken = this.authable.getAccessToken()
} = payload
let accessTokenPromise = accessToken
if (!isPromise(accessTokenPromise)) {
accessTokenPromise = Promise.resolve(accessToken)
}
raw = accessTokenPromise
.then(access_token => {
const finalPayload = {
access_token
}
return this._logout(finalPayload)
})
}
return raw
.catch(error => this.onError('logout', error))
}
prototype._refresh = function (sentIn) {
const {
refresh_token
} = sentIn
const url = this.getUrl('/api/v1/oauth/token')
const payload = {
url,
method: 'POST',
requestType: 'json',
data: {
grant_type: 'refresh_token',
refresh_token,
...this.getAuthParams()
}
}
return this.fetchWrap(payload)
}
/**
* @typedef {object} RefreshReturn
* @property {string} accessToken - access token
* @property {string} refreshToken - refresh token
* @property {number} expiresIn - seconds until accessToken is expired
* @property {number} createdAt - access token create timestamp
* @property {string} isAdmin - whether or not user is admin
*/
/**
* @function refresh
* @param {object} [payload] - The refresh payload
* @param {string} [payload.refreshToken=this.authable.getRefreshToken()] - refreshToken to process
* @returns {RefreshReturn} refresh return
* @example
*
* refresh({
* refreshToken: '12345'
* })
*/
prototype.refresh = function (payload = {}) {
let raw = payload
if (!isPromise(raw)) {
const {
refreshToken = this.authable.getRefreshToken()
} = payload
let refreshTokenPromise = refreshToken
if (!isPromise(refreshTokenPromise)) {
refreshTokenPromise = Promise.resolve(refreshToken)
}
raw = refreshTokenPromise
.then(refresh_token => {
const finalPayload = {
refresh_token
}
return this._refresh(finalPayload)
})
}
return raw
.then(res => this.normalizeLoginableReturn(res.data))
.then(data => this.authable.onRefresh(data))
.catch(error => this.onError('refresh', error))
}
}
export default ZEventsApiAuth