Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 153 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
"@tanstack/vue-virtual": "^3.9.0",
"dompurify": "^3.1.6",
"oidc-spa": "^10.0.8",
"pinia": "^3.0.4",
"quill": "^1.3.7",
"vue": "^3.4.0"
"vue": "^3.4.0",
"vue-router": "^4.6.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
Expand Down
124 changes: 124 additions & 0 deletions src/stores/auth-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { initOidc, getOidc } from '../services/auth.js'
import { JMAPClient } from '../services/jmap.js'
import { JMAP_SERVER_URL } from '../defines.js'

export const useAuthStore = defineStore('auth', () => {
const client = ref(null)
const connected = ref(false)
const status = ref('Not connected.')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can be inferred based on an enum state I mentioned below.

const error = ref('')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be null.

const oidcReady = ref(false)
const oidcLoading = ref(true)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these states independant of each other? You can merge it into an enum. (You're not using typescript but you can still create a object with the values.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ex/

const OIDC_LOADING_STATE = {
  NOT_CONNECTED: 'not_connected',
  FAILED: 'failed',
  CONNECTING: 'connecting',
  READY: 'ready',
};

Set it in your code like so:

oidcLoadingStatus.value = OIDC_LOADING_STATE.CONNECTING;

Then in your l10n you can simply have a computed that returns return l10n(`status_codes.${oidcLoadingStatus}`).


const serverName = computed(() => {
try {
return new URL(JMAP_SERVER_URL).hostname
} catch {
return JMAP_SERVER_URL
}
})

async function initializeOidc() {
try {
const oidc = await initOidc()
oidcReady.value = true
oidcLoading.value = false
return oidc
} catch (e) {
console.error('OIDC initialization failed:', e)
oidcLoading.value = false
return null
}
}

async function connect(credentials) {
if (!credentials?.username || !credentials?.password) {
error.value = 'Username and password required.'
return false
}
error.value = ''
status.value = 'Connecting…'
try {
client.value = new JMAPClient({
baseUrl: JMAP_SERVER_URL,
username: credentials.username.trim(),
password: credentials.password,
})
await client.value.fetchSession()
localStorage.setItem('jmap.username', credentials.username.trim())
connected.value = true
document.body.classList.add('connected')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be inferred based on the oidc status enum on whatever is consuming this store. No dom manip in stores!

return true
} catch (e) {
status.value = 'Failed.'
error.value =
e.message +
(e.message?.includes('Failed to fetch')
? '\nLikely CORS/network issue.'
: '')
return false
}
}

async function connectWithOAuth(oidc) {
error.value = ''
status.value = 'Connecting with OAuth…'
try {
client.value = new JMAPClient({
baseUrl: JMAP_SERVER_URL,
getToken: async () => {
const { accessToken } = await oidc.getTokens()
return accessToken
},
})
await client.value.fetchSession()
connected.value = true
document.body.classList.add('connected')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be inferred based on the oidc status enum on whatever is consuming this store. No dom manip in stores!

return true
} catch (e) {
status.value = 'Failed.'
error.value =
e.message +
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

es6 string literals pls.

(e.message?.includes('Failed to fetch')
? '\nLikely CORS/network issue.'
: '')
return false
}
}

async function handleOAuthLogin() {
const oidc = getOidc()
if (!oidc) return false
if (!oidc.isUserLoggedIn) {
await oidc.login()
return false
}
return await connectWithOAuth(oidc)
}

async function handleLogout() {
const oidc = getOidc()
if (oidc && oidc.isUserLoggedIn) {
await oidc.logout({ redirectTo: 'home' })
} else {
window.location.reload()
}
}

return {
client,
connected,
status,
error,
oidcReady,
oidcLoading,
serverName,
initializeOidc,
connect,
connectWithOAuth,
handleOAuthLogin,
handleLogout,
}
})
Loading