mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-07-29 23:25:47 +02:00
oauth2: Implement PKCE
- Also fixed URLs, the / after ${this.serverConfig.address} had to be removed, because during Connection flow the URL is now (re)set correctly uniformly - Removed now uneccessary callback? parameter from first auth request
This commit is contained in:
parent
2984398051
commit
8d8782a5a9
1 changed files with 29 additions and 5 deletions
|
@ -99,7 +99,8 @@ export default {
|
||||||
showAddCustomHeaders: false,
|
showAddCustomHeaders: false,
|
||||||
authMethods: [],
|
authMethods: [],
|
||||||
oauth: {
|
oauth: {
|
||||||
state: null
|
state: null,
|
||||||
|
verifier: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -226,9 +227,32 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async oauthRequest(url) {
|
async oauthRequest(url) {
|
||||||
|
// Generate oauth2 PKCE challenge
|
||||||
|
// In accordance to RFC 7636 Section 4
|
||||||
|
function base64URLEncode(arrayBuffer) {
|
||||||
|
let base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)))
|
||||||
|
return base64String
|
||||||
|
.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)
|
||||||
|
window.crypto.getRandomValues(randomBuffer)
|
||||||
|
const verifier = base64URLEncode(randomBuffer)
|
||||||
|
|
||||||
|
const challengeBuffer = await sha256(randomBuffer)
|
||||||
|
const challenge = base64URLEncode(challengeBuffer)
|
||||||
|
|
||||||
|
this.oauth.verifier = verifier
|
||||||
|
|
||||||
|
|
||||||
// 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
|
||||||
// We dont need the callback parameter strictly speaking, but we must provide something or passport will error out as it seems to always expect it
|
const backendEndpoint = `${url}auth/openid?code_challenge=${challenge}&code_challenge_method=S256&isRest=true`
|
||||||
const backendEndpoint = `${url}/auth/openid?callback=${encodeURIComponent('/login')}&isRest=true`
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await CapacitorHttp.get({
|
const response = await CapacitorHttp.get({
|
||||||
|
@ -259,7 +283,7 @@ export default {
|
||||||
},
|
},
|
||||||
async oauthExchangeCodeForToken(code, state) {
|
async oauthExchangeCodeForToken(code, state) {
|
||||||
// We need to read the url directly from this.serverConfig.address as the callback which is called via the external browser does not pass us that info
|
// We need to read the url directly from this.serverConfig.address as the callback which is called via the external browser does not pass us that info
|
||||||
const backendEndpoint = `${this.serverConfig.address}/auth/openid/callback?state=${encodeURIComponent(state)}&code=${encodeURIComponent(code)}`
|
const backendEndpoint = `${this.serverConfig.address}auth/openid/callback?state=${encodeURIComponent(state)}&code=${encodeURIComponent(code)}&code_verifier=${encodeURIComponent(this.oauth.verifier)}`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// We can close the browser at this point (does not work on Android)
|
// We can close the browser at this point (does not work on Android)
|
||||||
|
@ -701,7 +725,7 @@ export default {
|
||||||
this.error = null
|
this.error = null
|
||||||
this.processing = true
|
this.processing = true
|
||||||
|
|
||||||
const authRes = await this.postRequest(`${this.serverConfig.address}/api/authorize`, null, { Authorization: `Bearer ${this.serverConfig.token}` }).catch((error) => {
|
const authRes = await this.postRequest(`${this.serverConfig.address}api/authorize`, null, { Authorization: `Bearer ${this.serverConfig.token}` }).catch((error) => {
|
||||||
console.error('[ServerConnectForm] Server auth failed', error)
|
console.error('[ServerConnectForm] Server auth failed', error)
|
||||||
const errorMsg = error.message || error
|
const errorMsg = error.message || error
|
||||||
this.error = 'Failed to authorize'
|
this.error = 'Failed to authorize'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue