I am implementing a oAuth login for a user for the firebase platform.
All works fine except if the user has disabled cross domain cookies.
Here is what I did.
- From my domain/app the user gets redirected to a cloud function.
- The could function sets the
state
cookie and redirects the user to the oAuth provider. - The user signs in to the oAuth provider and gets redirected back to another function to get the code etc. And here is the problem
On step 3 above the function cannot read any cookie if the user has disabled the cross domain party cookies from his browser. Both functions are on the same domain as seen below in the screenshot.
Is there any way I can remedy this issue? Am I doing something wrong in my approach?
I cannot understand why the 2 functions are treated as crossdomain.
Update to include more info
Request:
Request URL: https://europe-west2-quantified-self-io.cloudfunctions.net/authRedirect
Request Method: GET
Status Code: 302
Remote Address: [2a00:1450:4007:811::200e]:443
Referrer Policy: no-referrer-when-downgrade
Request Headers
:authority: europe-west2-quantified-self-io.cloudfunctions.net
:method: GET
:path: /authRedirect
:scheme: https
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8
cookie: signInWithService=false; state=877798d3672e7d6fa9588b03f1e26794f4ede3a0
dnt: 1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Response Headers
alt-svc: quic=":443"; ma=2592000; v="46,43,39"
cache-control: private
content-encoding: gzip
content-length: 218
content-type: text/html; charset=utf-8
date: Sat, 03 Aug 2019 08:55:18 GMT
function-execution-id: c8rjc7xnvoy8
location: https://cloudapi-oauth.suunto.com/oauth/authorize?response_type=code&client_id=xxx&redirect_uri=&scope=workout&state=1c8073866d1ffaacf2d4709090ad099872718afa
server: Google Frontend
set-cookie: state=1c8073866d1ffaacf2d4709090ad099872718afa; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
set-cookie: signInWithService=false; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
status: 302
vary: Accept
x-cloud-trace-context: 99a93680a17770f848f200a9e729b122;o=1
x-powered-by: Express
After that and once the user returns from the service he authenticated against the code that parses the cookies (or the function that handles that) is:
export const authToken = functions.region('europe-west2').https.onRequest(async (req, res) => {
const oauth2 = suuntoAppAuth();
cookieParser()(req, res, async () => {
try {
const currentDate = new Date();
const signInWithService = req.cookies.signInWithService === 'true';
console.log('Should sign in:', signInWithService);
console.log('Received verification state:', req.cookies.state);
console.log('Received state:', req.query.state);
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
console.log('Received auth code:', req.query.code);
const results = await oauth2.authorizationCode.getToken({
code: req.query.code,
redirect_uri: determineRedirectURI(req), // @todo fix,
});
// console.log('Auth code exchange result received:', results);
// We have an access token and the user identity now.
const accessToken = results.access_token;
const suuntoAppUserName = results.user;
// Create a Firebase account and get the Custom Auth Token.
let firebaseToken;
if (signInWithService) {
firebaseToken = await createFirebaseAccount(suuntoAppUserName, accessToken);
}
return res.jsonp({
firebaseAuthToken: firebaseToken,
serviceAuthResponse: <ServiceTokenInterface>{
accessToken: results.access_token,
refreshToken: results.refresh_token,
tokenType: results.token_type,
expiresAt: currentDate.getTime() + (results.expires_in * 1000),
scope: results.scope,
userName: results.user,
dateCreated: currentDate.getTime(),
dateRefreshed: currentDate.getTime(),
},
serviceName: ServiceNames.SuuntoApp
});
} catch (error) {
return res.jsonp({
error: error.toString(),
});
}
});
});
The above code does not find a cookie with the name state
So it fails here
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
Did a little more search here is some more info.
The example I based on https://github.com/firebase/functions-samples/tree/master/instagram-auth
Looks like other users suffer from the same issue https://github.com/firebase/functions-samples/issues/569
I opened also this issue https://github.com/firebase/firebase-functions/issues/544