Check for duplicate config when authenticating with openid

This commit is contained in:
advplyr 2023-11-11 13:55:32 -06:00
parent 5fdd0c6672
commit 4f994072ad

View file

@ -197,10 +197,7 @@ export default {
this.oauth.state = state this.oauth.state = state
const host = `https://${redirectUrl.host}` const host = `https://${redirectUrl.host}`
const buildUrl = `${host}${redirectUrl.pathname}?response_type=code` + const buildUrl = `${host}${redirectUrl.pathname}?response_type=code` + `&client_id=${encodeURIComponent(client_id)}&scope=${encodeURIComponent(scope)}&state=${encodeURIComponent(state)}` + `&redirect_uri=${encodeURIComponent('audiobookshelf://oauth')}` + `&code_challenge=${encodeURIComponent(this.oauth.challenge)}&code_challenge_method=S256`
`&client_id=${encodeURIComponent(client_id)}&scope=${encodeURIComponent(scope)}&state=${encodeURIComponent(state)}` +
`&redirect_uri=${encodeURIComponent('audiobookshelf://oauth')}` +
`&code_challenge=${encodeURIComponent(this.oauth.challenge)}&code_challenge_method=S256`
// example url for authentik // example url for authentik
// const authURL = "https://authentik/application/o/authorize/?response_type=code&client_id=41cd96f...&redirect_uri=audiobookshelf%3A%2F%2Foauth&scope=openid%20openid%20email%20profile&state=asdds..." // const authURL = "https://authentik/application/o/authorize/?response_type=code&client_id=41cd96f...&redirect_uri=audiobookshelf%3A%2F%2Foauth&scope=openid%20openid%20email%20profile&state=asdds..."
@ -225,10 +222,7 @@ export default {
// In accordance to RFC 7636 Section 4 // In accordance to RFC 7636 Section 4
function base64URLEncode(arrayBuffer) { function base64URLEncode(arrayBuffer) {
let base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer))) let base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)))
return base64String return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/g, '')
} }
async function sha256(plain) { async function sha256(plain) {
@ -240,7 +234,7 @@ export default {
function generateRandomString() { function generateRandomString() {
var array = new Uint32Array(42) var array = new Uint32Array(42)
window.crypto.getRandomValues(array) window.crypto.getRandomValues(array)
return Array.from(array, dec => ('0' + dec.toString(16)).slice(-2)).join('') // hex return Array.from(array, (dec) => ('0' + dec.toString(16)).slice(-2)).join('') // hex
} }
const verifier = generateRandomString() const verifier = generateRandomString()
@ -250,7 +244,6 @@ export default {
this.oauth.verifier = verifier this.oauth.verifier = verifier
this.oauth.challenge = challenge this.oauth.challenge = challenge
// set parameter isRest to true, so the backend wont attempt a redirect after we call backend:/callback in exchangeCodeForToken // set parameter isRest to true, so the backend wont attempt a redirect after we call backend:/callback in exchangeCodeForToken
const backendEndpoint = `${url}auth/openid?code_challenge=${challenge}&code_challenge_method=S256&isRest=true` const backendEndpoint = `${url}auth/openid?code_challenge=${challenge}&code_challenge_method=S256&isRest=true`
@ -346,7 +339,7 @@ export default {
if (this.$platform === 'ios' || this.$platform === 'web') { if (this.$platform === 'ios' || this.$platform === 'web') {
await Browser.close() await Browser.close()
} }
} catch(error) {} // No Error handling needed } catch (error) {} // No Error handling needed
try { try {
const response = await CapacitorHttp.get({ const response = await CapacitorHttp.get({
@ -364,6 +357,11 @@ export default {
throw new Error('Authentication failed with the provided token.') throw new Error('Authentication failed with the provided token.')
} }
const duplicateConfig = this.serverConnectionConfigs.find((scc) => scc.address === this.serverConfig.address && scc.username === payload.user.username)
if (duplicateConfig) {
throw new Error('Config already exists for this address and username.')
}
this.setUserAndConnection(payload) this.setUserAndConnection(payload)
} catch (error) { } catch (error) {
console.error('[SSO] Error in exchangeCodeForToken: ', error) console.error('[SSO] Error in exchangeCodeForToken: ', error)
@ -640,8 +638,7 @@ export default {
console.error(`[ServerConnectForm] Server redirected somewhere else (to ${currentAddressUrl.hostname})`) console.error(`[ServerConnectForm] Server redirected somewhere else (to ${currentAddressUrl.hostname})`)
return false return false
} // We don't allow a redirection back from https to http if the user used https:// explicitly } // We don't allow a redirection back from https to http if the user used https:// explicitly
else if (protocolProvided && else if (protocolProvided && initialAddressWithProtocol.startsWith('https://') && currentAddressUrl.protocol === 'http') {
initialAddressWithProtocol.startsWith('https://') && currentAddressUrl.protocol === 'http') {
this.error = `You specified https:// but the Server redirected back to plain http` this.error = `You specified https:// but the Server redirected back to plain http`
console.error(`[ServerConnectForm] User specified https:// but server redirected to http`) console.error(`[ServerConnectForm] User specified https:// but server redirected to http`)
return false return false
@ -684,9 +681,11 @@ export default {
if (error.code === 404) { if (error.code === 404) {
this.error = `This does not seem to be an Audiobookshelf server. (Error: 404)` this.error = `This does not seem to be an Audiobookshelf server. (Error: 404)`
} else if (typeof error.code === "number") { // Error with HTTP Code } else if (typeof error.code === 'number') {
// Error with HTTP Code
this.error = `Failed to retrieve status of server: ${error.code}` this.error = `Failed to retrieve status of server: ${error.code}`
} else { // error is usually a meaningful error like "Server timed out" } else {
// error is usually a meaningful error like "Server timed out"
this.error = `Failed to contact server. (${error})` this.error = `Failed to contact server. (${error})`
} }
}, },
@ -712,8 +711,8 @@ export default {
// We only retry when the user did not specify a protocol // We only retry when the user did not specify a protocol
// Also for security reasons, we only retry when the https request did not // Also for security reasons, we only retry when the https request did not
// return a http status code (so only retry when the TCP connection could not be established) // return a http status code (so only retry when the TCP connection could not be established)
if (shouldRetryWithHttp && (typeof error.code !== "number")) { if (shouldRetryWithHttp && typeof error.code !== 'number') {
console.log("[ServerConnectForm] https failed, trying to connect with http...") console.log('[ServerConnectForm] https failed, trying to connect with http...')
const validatedHttpUrl = this.validateServerUrl(address, 'http:') const validatedHttpUrl = this.validateServerUrl(address, 'http:')
if (validatedHttpUrl) { if (validatedHttpUrl) {
return await this.getServerAddressStatus(validatedHttpUrl) return await this.getServerAddressStatus(validatedHttpUrl)
@ -731,9 +730,7 @@ export default {
* @returns {string} The address with a protocol prepended if it was missing. * @returns {string} The address with a protocol prepended if it was missing.
*/ */
prependProtocolIfNeeded(address) { prependProtocolIfNeeded(address) {
return address.startsWith('http://') || address.startsWith('https://') return address.startsWith('http://') || address.startsWith('https://') ? address : `https://${address}`
? address
: `https://${address}`
}, },
/** /**
* Compares two semantic versioning strings to determine if the current version meets * Compares two semantic versioning strings to determine if the current version meets