Skip to content

Commit

Permalink
Add ability to sign in via SSO (#73)
Browse files Browse the repository at this point in the history
* Add ability to sign in via SSO

* Remove unused commit in store action
  • Loading branch information
ajm13 authored Feb 21, 2024
1 parent 443ce03 commit e823301
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 13 deletions.
15 changes: 14 additions & 1 deletion server/ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,23 @@
</template>

<script>
import { mapState } from 'vuex'
import UploadManager from '@/components/UploadManager'
export default {
components: { UploadManager }
components: { UploadManager },
computed: mapState(['unique']),
created() {
const { query, path } = this.$route
if (!this.unique && query.auth) {
const data = { token: query.auth }
this.$store.commit('PATCH_USER', data)
this.$store.dispatch('refresh')
this.$router.replace({ path })
}
}
}
</script>

Expand Down
21 changes: 17 additions & 4 deletions server/ui/src/api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import store from '@/store'
const LOGIN_API = 'https://api.nanome.ai/user'
const AUTH_API = 'https://api.nanome.ai'

function replacePath(path) {
return path
Expand All @@ -13,7 +13,7 @@ function addSlash(path) {
}

async function request(url, options = {}) {
if (store.state.token && !url.startsWith(LOGIN_API)) {
if (store.state.token && !url.startsWith(AUTH_API)) {
options.headers = Object.assign({}, options.headers, {
Authorization: 'Bearer ' + store.state.token
})
Expand Down Expand Up @@ -82,18 +82,31 @@ const API = {
source: 'web:plugin-vault'
}

return request(`${LOGIN_API}/login`, {
return request(`${AUTH_API}/user/login`, {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
body: JSON.stringify(body)
})
},

loginSSO(email, redirect) {
const params = new URLSearchParams({
email,
redirect,
cookie: false
})

return request(`${AUTH_API}/sso/login?` + params, {
headers: { 'Content-Type': 'application/json' },
method: 'GET'
})
},

refresh() {
const token = store.state.token
if (!token) return {}

return request(`${LOGIN_API}/session`, {
return request(`${AUTH_API}/user/session`, {
headers: { Authorization: `Bearer ${token}` }
})
},
Expand Down
84 changes: 77 additions & 7 deletions server/ui/src/components/Modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,60 @@
type="password"
/>

<p v-if="error" class="text-red-500">incorrect login or password</p>
<p v-if="error" class="text-red-500">{{ error }}</p>

<p>
<a
key="login-forgot"
href="https://home.nanome.ai/login/forgot"
target="_blank"
class="link text-blue-500"
>forgot password?</a
>
forgot password?
or

<a
key="login-sso"
class="link text-blue-500"
@click.stop="options.type = 'login-sso'"
>log in with SSO</a
>
</p>

<p></p>

<p class="text-xs text-gray-700">
Don't have a Nanome account?
<a
href="https://home.nanome.ai/register"
target="_blank"
class="link text-blue-500"
>
Register here
</a>
</p>
</template>

<template v-else-if="options.type === 'login-sso'">
<input
ref="login"
v-model="input1"
:class="{ 'border-red-500': error }"
placeholder="email"
type="text"
/>

<p v-if="error" class="text-red-500">
{{ error }}
</p>

<p>
<a
key="login-username"
class="link text-blue-500"
@click.stop="options.type = 'login'"
>
log in with username
</a>
</p>

Expand Down Expand Up @@ -107,7 +152,7 @@ const deferred = () => {
export default {
data: () => ({
showing: false,
error: false,
error: null,
loading: false,
options: { ...defaults },
input1: '',
Expand All @@ -121,14 +166,22 @@ export default {
if (this.options.type === 'login') {
return !this.input1 || !this.input2
} else if (this.options.type === 'prompt') {
} else if (['login-sso', 'prompt'].includes(this.options.type)) {
return !this.input1
}
return false
}
},
watch: {
'options.type'() {
this.error = null
this.input1 = ''
this.input2 = ''
}
},
mounted() {
document.body.addEventListener('keydown', this.onKeydown)
},
Expand Down Expand Up @@ -229,6 +282,9 @@ export default {
} else if (this.options.type === 'login') {
this.attemptLogin()
return
} else if (this.options.type === 'login-sso') {
this.attemptLoginSSO()
return
}
this.deferred.resolve(data)
Expand All @@ -237,7 +293,7 @@ export default {
async attemptLogin() {
const deferred = this.deferred
this.error = false
this.error = null
const creds = {
username: this.input1,
Expand All @@ -252,6 +308,9 @@ export default {
break
} catch (e) {
this.loading = false
this.error = e.message
if (!this.error.includes('2FA')) break
creds.tfa_code = await this.prompt({
title: 'Enter 2FA code',
body: 'Or use a one time recovery code',
Expand All @@ -269,8 +328,19 @@ export default {
if (success) {
deferred.resolve(true)
this.reset()
} else {
this.error = true
}
},
async attemptLoginSSO() {
this.error = null
this.loading = true
try {
const url = await this.$store.dispatch('loginSSO', this.input1)
window.open(url, '_self')
} catch (e) {
this.loading = false
this.error = e.message
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion server/ui/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,20 @@ const actions = {

async login({ commit }, creds) {
const data = await API.login(creds)
if (data.code === 401) {
if (data.code !== 200) {
throw new Error(data.error.message)
}
return saveSession(commit, data)
},

async loginSSO(_, email) {
const data = await API.loginSSO(email, window.location.href)
if (data.code !== 200) {
throw new Error(data.error.message)
}
return data.results
},

async refresh({ commit }) {
const data = await API.refresh()
return saveSession(commit, data)
Expand Down

0 comments on commit e823301

Please sign in to comment.