mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-30 15:17:14 +02:00
oauth2: Fix PKCE
- Send challenge to the SSO provider itself too - Fix challenge generation - Removed semicolons
This commit is contained in:
@@ -103,6 +103,7 @@ export default {
|
|||||||
oauth: {
|
oauth: {
|
||||||
state: null,
|
state: null,
|
||||||
verifier: null,
|
verifier: null,
|
||||||
|
challenge: null,
|
||||||
buttonText: 'Login with OpenID'
|
buttonText: 'Login with OpenID'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +197,10 @@ 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` + `&client_id=${encodeURIComponent(client_id)}&scope=${encodeURIComponent(scope)}&state=${encodeURIComponent(state)}` + `&redirect_uri=${encodeURIComponent('audiobookshelf://oauth')}`
|
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`
|
||||||
|
|
||||||
// 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..."
|
||||||
@@ -224,21 +228,27 @@ export default {
|
|||||||
return base64String
|
return base64String
|
||||||
.replace(/\+/g, '-')
|
.replace(/\+/g, '-')
|
||||||
.replace(/\//g, '_')
|
.replace(/\//g, '_')
|
||||||
.replace(/=/g, '')
|
.replace(/=+$/g, '')
|
||||||
}
|
|
||||||
async function sha256(buffer) {
|
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer)
|
|
||||||
return new Uint8Array(hashBuffer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const randomBuffer = new Uint8Array(64)
|
async function sha256(plain) {
|
||||||
window.crypto.getRandomValues(randomBuffer)
|
const encoder = new TextEncoder()
|
||||||
const verifier = base64URLEncode(randomBuffer)
|
const data = encoder.encode(plain)
|
||||||
|
return await window.crypto.subtle.digest('SHA-256', data)
|
||||||
|
}
|
||||||
|
|
||||||
const challengeBuffer = await sha256(randomBuffer)
|
function generateRandomString() {
|
||||||
const challenge = base64URLEncode(challengeBuffer)
|
var array = new Uint32Array(42)
|
||||||
|
window.crypto.getRandomValues(array)
|
||||||
|
return Array.from(array, dec => ('0' + dec.toString(16)).slice(-2)).join('') // hex
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifier = generateRandomString()
|
||||||
|
|
||||||
|
const challenge = base64URLEncode(await sha256(verifier))
|
||||||
|
|
||||||
this.oauth.verifier = verifier
|
this.oauth.verifier = verifier
|
||||||
|
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
|
||||||
@@ -361,6 +371,7 @@ export default {
|
|||||||
} finally {
|
} finally {
|
||||||
// We don't need the oauth verifier any more
|
// We don't need the oauth verifier any more
|
||||||
this.oauth.verifier = null
|
this.oauth.verifier = null
|
||||||
|
this.oauth.challenge = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addCustomHeaders() {
|
addCustomHeaders() {
|
||||||
@@ -646,9 +657,9 @@ export default {
|
|||||||
console.error('[ServerConnectForm] Received as response from Server:\n', statusData)
|
console.error('[ServerConnectForm] Received as response from Server:\n', statusData)
|
||||||
return false
|
return false
|
||||||
} else if (!this.isValidVersion(statusData.data.serverVersion, requiredServerVersion)) {
|
} else if (!this.isValidVersion(statusData.data.serverVersion, requiredServerVersion)) {
|
||||||
this.error = `Server version is below minimum required version of ${requiredServerVersion} (${statusData.data.serverVersion})`;
|
this.error = `Server version is below minimum required version of ${requiredServerVersion} (${statusData.data.serverVersion})`
|
||||||
console.error('[ServerConnectForm] Server version is too low: ', statusData.data.serverVersion);
|
console.error('[ServerConnectForm] Server version is too low: ', statusData.data.serverVersion)
|
||||||
return false;
|
return false
|
||||||
} else if (!statusData.data.isInit) {
|
} else if (!statusData.data.isInit) {
|
||||||
this.error = 'Server is not initialized'
|
this.error = 'Server is not initialized'
|
||||||
return false
|
return false
|
||||||
@@ -734,15 +745,15 @@ export default {
|
|||||||
* to the minimum version, false otherwise.
|
* to the minimum version, false otherwise.
|
||||||
*/
|
*/
|
||||||
isValidVersion(currentVersion, minVersion) {
|
isValidVersion(currentVersion, minVersion) {
|
||||||
const currentParts = currentVersion.split('.').map(Number);
|
const currentParts = currentVersion.split('.').map(Number)
|
||||||
const minParts = minVersion.split('.').map(Number);
|
const minParts = minVersion.split('.').map(Number)
|
||||||
|
|
||||||
for (let i = 0; i < minParts.length; i++) {
|
for (let i = 0; i < minParts.length; i++) {
|
||||||
if (currentParts[i] > minParts[i]) return true;
|
if (currentParts[i] > minParts[i]) return true
|
||||||
if (currentParts[i] < minParts[i]) return false;
|
if (currentParts[i] < minParts[i]) return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true
|
||||||
},
|
},
|
||||||
async submitAuth() {
|
async submitAuth() {
|
||||||
if (!this.networkConnected) return
|
if (!this.networkConnected) return
|
||||||
|
|||||||
Reference in New Issue
Block a user