Google & Facebook OAuth without firebase

Hello, I am trying to make authentication system with google oauth. But as firebase is not free to use after certain request, that's why i want to do it by implementing the endpoint which i do in my react application. For this case i am using a package named @react-oauth/google But this is not working properly. I got some error like
Refused to load the script 'https://accounts.google.com/gsi/client' because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:*". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
Refused to load the script 'https://accounts.google.com/gsi/client' because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:*". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
Can anyone help me how to fix it? Or is there any blog or documentation for implement google and facebook oauth?
12 Replies
Lionel
Lionel•9mo ago
@Asif Did you try to adjust the content_security_policy rules for the extension in package.json ?
Asif
Asif•9mo ago
@Lionel Yeah. I have tried this. But this doesn't work for me. I have implemented google oauth by calling the endpoint direct in my background. Like this:
const clientId = encodeURIComponent(process.env.PLASMO_PUBLIC_GOOGLE_CLIENT_ID)
const responseType = encodeURIComponent("token id_token")
const redirecturi = encodeURIComponent(
`https://${process.env.PLASMO_PUBLIC_EXTENSION_ID}.chromiumapp.org`
)
const prompt = encodeURIComponent("consent")
const scope = encodeURIComponent("openid profile email")
const state = encodeURIComponent("pass-through value")

const createOauth2Url = () => {
const nonce = encodeURIComponent(
Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15)
)

const requesturl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&response_type=${responseType}&redirect_uri=${redirecturi}&scope=${scope}&prompt=${prompt}&state=${state}&nonce=${nonce}`

return requesturl
}

const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
const callbackurl = await chrome.identity.launchWebAuthFlow({
url: createOauth2Url(),
interactive: true
})

console.log("Getting started soon...")

const searchParams = new URLSearchParams(callbackurl.split("#")[1])
const accessToken = searchParams.get("access_token")
const idToken = searchParams.get("id_token")

const tokenResponse = await fetch(
`https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${idToken}`
)

if (!tokenResponse.ok) {
res.send({
signedIn: isSignedin,
message: "Error when getting user"
})
return
}

const userInfo = await tokenResponse.json()

}
const clientId = encodeURIComponent(process.env.PLASMO_PUBLIC_GOOGLE_CLIENT_ID)
const responseType = encodeURIComponent("token id_token")
const redirecturi = encodeURIComponent(
`https://${process.env.PLASMO_PUBLIC_EXTENSION_ID}.chromiumapp.org`
)
const prompt = encodeURIComponent("consent")
const scope = encodeURIComponent("openid profile email")
const state = encodeURIComponent("pass-through value")

const createOauth2Url = () => {
const nonce = encodeURIComponent(
Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15)
)

const requesturl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&response_type=${responseType}&redirect_uri=${redirecturi}&scope=${scope}&prompt=${prompt}&state=${state}&nonce=${nonce}`

return requesturl
}

const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
const callbackurl = await chrome.identity.launchWebAuthFlow({
url: createOauth2Url(),
interactive: true
})

console.log("Getting started soon...")

const searchParams = new URLSearchParams(callbackurl.split("#")[1])
const accessToken = searchParams.get("access_token")
const idToken = searchParams.get("id_token")

const tokenResponse = await fetch(
`https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${idToken}`
)

if (!tokenResponse.ok) {
res.send({
signedIn: isSignedin,
message: "Error when getting user"
})
return
}

const userInfo = await tokenResponse.json()

}
This is just the basic code which is responsible for oauth
McJezus
McJezus•9mo ago
@Asif Which browser are you using? There is a bug in some browsers that use Chromium, like Brave, where it fails when encoding the URL for the OAuth, so it fails with that error. When I moved to Chrome specifically it worked. If you still have a problem I will explain to you what I did for Google OAuth :)
Asif
Asif•7mo ago
@McJezus I am using chrome. Can you share what you did?
McJezus
McJezus•7mo ago
Sure thing. I am using this in the popup directly and not in the background:
const handleOAuthLogin = async () => {
if (busy) return
setBusy(true)
chrome.identity.getAuthToken(
{
interactive: true
},
(token) => {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message)
return
}
// This is a function I am using to call my backend with the token,
// which will then be exchanged for an oauth token
OAuthLogin({
provider: "google",
token
})
.unwrap()
.catch((err) => {
toast.error(err.data.message)
})
.finally(() => {
setBusy(false)
})
}
)
}
const handleOAuthLogin = async () => {
if (busy) return
setBusy(true)
chrome.identity.getAuthToken(
{
interactive: true
},
(token) => {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message)
return
}
// This is a function I am using to call my backend with the token,
// which will then be exchanged for an oauth token
OAuthLogin({
provider: "google",
token
})
.unwrap()
.catch((err) => {
toast.error(err.data.message)
})
.finally(() => {
setBusy(false)
})
}
)
}
Asif
Asif•7mo ago
@McJezus is the handleOAuthLogin function in your content ui? and can you show me your OAuthLogin?
McJezus
McJezus•7mo ago
No it is in my popup file. The OAuthLogin is a simple rtk mutation that calls a back-end route. It looks like this:
OAuthLogin: builder.mutation<LoginSuccessResponse, OAuthBody>({
query: (body) => ({
url: `${authRouteName}/oauth`,
method: "POST",
body,
}),
onQueryStarted: async (_args, { dispatch, queryFulfilled }) => {
try {
const result = await queryFulfilled;
if (result.data) {
// do something here if needed
}
} catch (e) {
// Catch the error
}
},
}),
OAuthLogin: builder.mutation<LoginSuccessResponse, OAuthBody>({
query: (body) => ({
url: `${authRouteName}/oauth`,
method: "POST",
body,
}),
onQueryStarted: async (_args, { dispatch, queryFulfilled }) => {
try {
const result = await queryFulfilled;
if (result.data) {
// do something here if needed
}
} catch (e) {
// Catch the error
}
},
}),
This can be exchanged for any function that makes a request to your back-end. It sends the token we received from the login to the back so it can exchange it for an access token.
Arcane
Arcane•7mo ago
@McJezus has reached level 1. GG!
Asif
Asif•7mo ago
@McJezus got it. thanks. i will try this approach
Arcane
Arcane•7mo ago
@Asif has reached level 3. GG!
YAGPDB.xyz
YAGPDB.xyz•7mo ago
Gave +1 Rep to @McJezus (current: #26 - 1)
McJezus
McJezus•7mo ago
@Asif I'm actually wrong in my explanation. The getAuthToken returns an OAuth2 token which is also cached by the identity API. This is given in the jsdoc for the function:
function chrome.identity.getAuthToken(details: chrome.identity.TokenDetails, callback: (token?: string, grantedScopes?: string[]) => void): void (+1 overload)
------------------------------------
Gets an OAuth2 access token using the client ID and scopes specified in the oauth2 section of manifest.json. The Identity API caches access tokens in memory, so it's ok to call getAuthToken non-interactively any time a token is required. The token cache automatically handles expiration. For a good user experience it is important interactive token requests are initiated by UI in your app explaining what the authorization is for. Failing to do this will cause your users to get authorization requests, or Chrome sign in screens if they are not signed in, with with no context. In particular, do not use getAuthToken interactively when your app is first launched. If callback is not provided, the function returns a Promise that resolves with the token.

@param details — Token options.

@param callback — Called with an OAuth2 access token as specified by the manifest, or undefined if there was an error.
function chrome.identity.getAuthToken(details: chrome.identity.TokenDetails, callback: (token?: string, grantedScopes?: string[]) => void): void (+1 overload)
------------------------------------
Gets an OAuth2 access token using the client ID and scopes specified in the oauth2 section of manifest.json. The Identity API caches access tokens in memory, so it's ok to call getAuthToken non-interactively any time a token is required. The token cache automatically handles expiration. For a good user experience it is important interactive token requests are initiated by UI in your app explaining what the authorization is for. Failing to do this will cause your users to get authorization requests, or Chrome sign in screens if they are not signed in, with with no context. In particular, do not use getAuthToken interactively when your app is first launched. If callback is not provided, the function returns a Promise that resolves with the token.

@param details — Token options.

@param callback — Called with an OAuth2 access token as specified by the manifest, or undefined if there was an error.
Want results from more Discord servers?
Add your server