Skip to content

Instantly share code, notes, and snippets.

@MarcelloTheArcane
Created March 4, 2021 10:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MarcelloTheArcane/ce0b3bf2efa2d0b23de3812ab1bf692e to your computer and use it in GitHub Desktop.
Save MarcelloTheArcane/ce0b3bf2efa2d0b23de3812ab1bf692e to your computer and use it in GitHub Desktop.
import axios from 'axios'
const VERSION = '1.0.0'
class NhostAuth {
constructor (baseUrl) {
this._baseUrl = baseUrl
this.version = VERSION
this._refreshTimeMargin = 45000
this._refreshTimeout = null
this._mounted()
}
async login (email, password) {
try {
const { data } = await axios.post(`${this._baseUrl}/auth/login`, {
email,
password,
cookie: false,
})
this._setRefreshToken(data.refresh_token)
this._setJWTToken(data.jwt_token)
this._setUser(data.user)
this.claims = this._parseJWT(data.jwt_token).payload['https://hasura.io/jwt/claims']
this._triggerRefresh(data.jwt_expires_in - this._refreshTimeMargin)
return Promise.resolve(true)
} catch (err) {
Promise.reject(err.message)
}
}
async register (email, password) {
try {
await axios.post(`${this._baseUrl}/auth/register`, {
email,
password,
})
Promise.resolve(true)
} catch (err) {
Promise.reject(err.message)
}
}
requestPasswordReset (email) {
return axios.post(`${this._baseUrl}/auth/change-password/request`, {
email,
})
}
resetPassword (ticket, password) {
return axios.post(`${this._baseUrl}/auth/change-password/change`, {
ticket,
password,
})
}
confirmEmail (ticket) {
return axios.get(`${this._baseUrl}/auth/activate?ticket=${ticket}`)
}
logout () {
clearTimeout(this._refreshTimeout)
this._refreshTimeout = null
this._setRefreshToken(null)
this._setJWTToken(null)
this._setUser(null)
}
isAuthenticated () {
const authenticated = !!(this._getRefreshToken() && this.getJWTToken() && this._validateJWT(this.getJWTToken()))
return authenticated
}
_parseJWT (JWT) {
const splitJWT = JWT.split('.')
if (splitJWT.length !== 3) {
console.error('Expected 3 parts in JWT, got ' + splitJWT.length)
return false
}
let header = null
let payload = null
const signature = splitJWT[2]
try {
header = JSON.parse(atob(splitJWT[0]))
payload = JSON.parse(atob(splitJWT[1]))
} catch (err) {
console.error('Error parsing JWT.')
return false
}
return {
header,
payload,
signature,
}
}
_validateJWT (JWT) {
const parsedJWT = this._parseJWT(JWT)
return parsedJWT && parsedJWT.header.alg === 'HS256' && Date.now() < (parsedJWT.payload.exp * 1000)
}
_triggerRefresh (timeout) {
this._refreshTimeout = setTimeout(async () => {
// Don't bother trying if refresh token is not there
if (this._getRefreshToken()) {
// Next timeout quickly, unless document is focussed. Then next timeout as per JWT.
let nextTimeout = this._refreshTimeMargin + 10000
if (document.hasFocus()) {
nextTimeout = await this._loginWithRefresh()
}
this._triggerRefresh(nextTimeout - this._refreshTimeMargin)
}
}, timeout)
}
async _loginWithRefresh () {
const { data } = await axios.get(`${this._baseUrl}/auth/token/refresh`, {
params: {
refresh_token: this._getRefreshToken()
}
})
this._setRefreshToken(data.refresh_token)
this._setJWTToken(data.jwt_token)
this._setUser(data.user)
return data.jwt_expires_in
}
_getRefreshToken () {
return localStorage.getItem(`tilde_auth_${this.version}_refresh_token`) || null
}
_setRefreshToken (value) {
if (value === null) {
localStorage.removeItem(`tilde_auth_${this.version}_refresh_token`)
} else {
localStorage.setItem(`tilde_auth_${this.version}_refresh_token`, value)
}
}
getJWTToken () {
return localStorage.getItem(`tilde_auth_${this.version}_jwt_token`) || null
}
_setJWTToken (value) {
if (value === null) {
localStorage.removeItem(`tilde_auth_${this.version}_jwt_token`)
} else {
localStorage.setItem(`tilde_auth_${this.version}_jwt_token`, value)
}
}
getUser () {
return JSON.parse(localStorage.getItem(`tilde_auth_${this.version}_user`)) || {}
}
_setUser (value) {
if (value === null) {
localStorage.removeItem(`tilde_auth_${this.version}_user`)
} else {
localStorage.setItem(`tilde_auth_${this.version}_user`, JSON.stringify(value))
}
}
_mounted () {
if (this.isAuthenticated()) {
const parsedJWT = this._parseJWT(this.getJWTToken())
this._triggerRefresh((parsedJWT.payload.exp * 1000) - this._refreshTimeMargin)
} else {
// Try to log on again
this._triggerRefresh(0)
}
}
}
class NhostStorage {
constructor (baseUrl) {
this._baseUrl = baseUrl
this.version = VERSION
}
put (path, file) {
const formData = new FormData()
formData.append('file', file)
if (path.charAt(0) === '/') {
path = path.slice(1)
}
return axios.put(`${this._baseUrl}/storage/o/${path}`, formData, {
headers: {
'Authorization': `Bearer ${this._getJWTToken()}`,
'Content-Type': 'multipart/form-data',
}
})
}
post (path, file) {
const formData = new FormData()
formData.append('file', file)
if (path.charAt(0) === '/') {
path = path.slice(1)
}
return axios.post(`${this._baseUrl}/storage/o/${path}`, formData, {
headers: {
'Authorization': `Bearer ${this._getJWTToken()}`,
'Content-Type': 'multipart/form-data',
}
})
}
delete (path) {
if (path.charAt(0) === '/') {
path = path.slice(1)
}
return axios.delete(`${this._baseUrl}/storage/o/${path}`, {
headers: {
'Authorization': `Bearer ${this._getJWTToken()}`,
}
})
}
get (path) {
if (path.charAt(0) === '/') {
path = path.slice(1)
}
return axios.get(`${this._baseUrl}/storage/o/${path}`, {
headers: {
'Authorization': `Bearer ${this._getJWTToken()}`,
}
})
}
getMetadata (path) {
if (path.charAt(0) === '/') {
path = path.slice(1)
}
return axios.get(`${this._baseUrl}/storage/m/${path}`, {
headers: {
'Authorization': `Bearer ${this._getJWTToken()}`,
}
})
}
_getJWTToken () {
return localStorage.getItem(`tilde_auth_${this.version}_jwt_token`) || null
}
}
const auth = new NhostAuth(process.env.VUE_APP_BACKEND_URL)
const storage = new NhostStorage(process.env.VUE_APP_BACKEND_URL)
export {
auth,
storage,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment