-
Notifications
You must be signed in to change notification settings - Fork 387
[6.1.0] Cannot complete OAuth process. Could not find an OAuth cookie for shop url #686
Comments
I just realised there is another issue #682 reported the same issue. |
Thanks for flagging this ... we need to document this a bit better. It's recommended to put the call to An example of this can be seen in an older non-Express version of the app template, here. |
@Copdate-Copdate, @kdeng - which browsers/OS/library versions are you using for this OAuth process? Also, what framework (Express, Koa, etc.), if any, are you using in your respective apps? |
@Copdate-Copdate Please share what you've done different by following that tutorial (in theory, the library does what the tutorial does!) From what you've shared, it seems you're building an Express app that uses the yarn create @shopify/app --name node-test-api-six --template https://github.com/Shopify/shopify-app-template-node#cli_three_api_six
cd node-test-api-six
yarn dev Just running this (with Node 19.5, Express 4.18.2, @shopify/shopify-api 6.1.0), installing it by the link given by the CLI (3.38), using Chrome 109 on macOS 13.2, it authenticated and installed on a shop at first attempt. 2023-01-26 19:57:45 | frontend |
2023-01-26 19:57:45 | frontend | > dev
2023-01-26 19:57:45 | frontend | > vite
2023-01-26 19:57:45 | frontend |
2023-01-26 19:57:45 | backend |
2023-01-26 19:57:45 | backend | > dev
2023-01-26 19:57:45 | backend | > cross-env NODE_ENV=development nodemon index.js --ignore ./frontend
2023-01-26 19:57:45 | backend |
2023-01-26 19:57:45 | backend | [nodemon] 2.0.20
2023-01-26 19:57:45 | backend | [nodemon] to restart at any time, enter `rs`
2023-01-26 19:57:45 | backend | [nodemon] watching path(s): *.*
2023-01-26 19:57:45 | backend | [nodemon] watching extensions: js,mjs,json
2023-01-26 19:57:45 | backend | [nodemon] starting `node index.js`
2023-01-26 19:57:45 | frontend |
2023-01-26 19:57:45 | frontend | vite v2.9.15 dev server running at:
2023-01-26 19:57:45 | frontend |
2023-01-26 19:57:45 | frontend | > Local: http://localhost:57405/
2023-01-26 19:57:45 | frontend | > Network: http://127.0.2.2:57405/
2023-01-26 19:57:45 | frontend | > Network: http://127.0.2.3:57405/
2023-01-26 19:57:45 | frontend | > Network: http://10.0.0.242:57405/
2023-01-26 19:57:45 | frontend | > Network: http://172.16.0.2:57405/
2023-01-26 19:57:45 | frontend |
2023-01-26 19:57:45 | frontend | ready in 278ms.
2023-01-26 19:57:45 | frontend |
2023-01-26 19:57:46 | backend | [shopify-api/INFO] version 6.1.0, environment Node v19.5.0
2023-01-26 19:57:57 | backend | [shopify-api/INFO] Beginning OAuth | {shop: xxxxxx.myshopify.com, isOnline: false, callbackPath: /api/auth/callback}
2023-01-26 19:58:11 | backend | [shopify-api/WARNING] [Deprecated | 7.0.0] The isOnline param is no longer required for auth callback
2023-01-26 19:58:11 | backend | [shopify-api/INFO] Completing OAuth | {shop: xxxxxx.myshopify.com}
2023-01-26 19:58:11 | backend | [shopify-api/INFO] Creating new session | {shop: xxxxxx.myshopify.com, isOnline: false}
2023-01-26 19:58:11 | backend | [shopify-api/INFO] Registering webhooks | {shop: xxxxxx.myshopify.com} The code for that app can be found here ... I'm trying to understand what's different between you app's code and that template and/or between environments. |
One additional thing to try ... add the following logger: {
level: LogSeverity.Debug, // import { LogSeverity } from "@shopify/shopify-api";
}, to the configuration you pass into the call to |
Using Koa as the platform, passing Going to dig in further, but this is extremely annoying. As an aside, why the heck are we supposed to store the session in the DB when all of the info is in the JWT? |
Anyway, I was able to get things working by implementing the oauth code manually. interface AuthConfig {
apiKey: string;
secret: string;
scopes: string[];
callbackPath: string;
homePath: string;
onAuthStart: (shop: string) => void | Promise<void>;
onAuthSuccess: (response: unknown) => void | Promise<void>;
}
export const shopify = ({
apiKey,
secret,
callbackPath,
scopes,
onAuthStart,
homePath,
onAuthSuccess
}: AuthConfig) => {
const verify = verifyWithKey(secret);
return {
async auth(ctx: Koa.Context, next: Koa.Next) {
const params = ctx.URL.searchParams;
const shop = params.get('shop');
const timestamp = params.get('timestamp');
const hmac = params.get('hmac');
if (!shop) {
ctx.throw(422, 'missing shop');
}
if (!timestamp) {
ctx.throw(422, 'missing timestamp');
}
if (!hmac) {
ctx.throw(422, 'missing hmac');
}
params.delete('hmac');
const query = params.toString();
if (!verify(query, hmac)) {
ctx.throw(401, `cant verify query ${query}`);
}
await onAuthStart(shop);
const shopifyUrl = shopifyAuthUrl({
apiKey,
scopes,
callbackPath,
shop,
host: ctx.host,
grantOptions: 'value',
});
ctx.redirect(shopifyUrl);
await next();
},
async callback(ctx: Koa.Context, next: Koa.Next) {
const params = ctx.URL.searchParams;
const shop = params.get('shop');
const host = params.get('host');
const hmac = params.get('hmac');
const code = params.get('code');
const grant = params.get('state');
if (typeof hmac !== 'string') {
ctx.throw(422, 'bad hmac');
}
if (!shop || !shop.match(/[a-zA-Z0-9][a-zA-Z0-9\-]*\.myshopify\.com/)) {
ctx.throw(422, 'bad shop');
}
if (typeof code !== 'string') {
ctx.throw(422, 'bad code');
}
if (typeof host !== 'string') {
ctx.throw(422, 'bad host')
}
params.delete('hmac');
const query = params.toString();
if (!verify(query, hmac)) {
ctx.throw(401);
}
const tokenParams = new URLSearchParams();
tokenParams.set('client_id', apiKey);
tokenParams.set('client_secret', secret);
tokenParams.set('code', code);
const tokenResponse = await fetch(
`https://${shop}/admin/oauth/access_token`,
{
method: 'post',
body: tokenParams,
}
);
if (!tokenResponse.ok) {
console.error(tokenResponse.status);
console.error(await tokenResponse.text());
ctx.throw(401);
}
const response = camelKeys(await tokenResponse.json());
await onAuthSuccess(response);
if (grant === 'value') {
const shopifyUrl = shopifyAuthUrl({
apiKey,
shop,
scopes,
callbackPath,
host: ctx.host,
grantOptions: 'per-user',
})
ctx.redirect(shopifyUrl);
await next();
return;
}
const isEmbedded = params.get('embedded') === "1";
if (isEmbedded) {
const shopifyHost = Buffer.from(host, 'base64').toString();
const redirect = `https://${shopifyHost}/apps/${apiKey}?host=${host}&shop=${shop}`;
ctx.redirect(redirect);
await next();
return;
}
ctx.redirect(`/${homePath}?shop=${shop}&host=${encodeURIComponent(host)}`);
await next();
return;
}
}
};
type UrlProps = {
apiKey: string;
grantOptions: 'per-user' | 'value';
callbackPath: string;
shop: string;
host: string;
scopes: string[];
}
const shopifyAuthUrl = (props: UrlProps) => {
const shopifyParams = new URLSearchParams();
shopifyParams.set('client_id', props.apiKey);
shopifyParams.set('scope', props.scopes.join(','));
shopifyParams.set('redirect_uri', `https://${props.host}/${props.callbackPath}`)
shopifyParams.set('grant_options[]', props.grantOptions);
shopifyParams.set('state', props.grantOptions);
return `https://${props.shop}/admin/oauth/authorize?${shopifyParams}`;
}; The only thing missing is the code for |
Catching the CookieNotFound error and then redirecting to Shopify again (and back) solves the problem for me. Found it in an example. try {
const session = (await this._shopifyAPI.auth.callback({
rawRequest: params.request,
rawResponse: params.response,
})).session;
return session?.toObject();
} catch (e) {
if (e instanceof InvalidOAuthError) {
throw new BadRequestException();
} else if (e instanceof CookieNotFound) {
await this._shopifyAPI.auth.begin({
shop: params.shop,
isOnline: false,
callbackPath: params.callback,
rawRequest: params.request,
rawResponse: params.response,
});
return undefined;
} else {
throw e;
}
} |
@mkevinosullivan Re-creating the session object led me to jumping through a few hoops and remembering what you guys used the |
Hitting the same issue, but the fix to catch the CookieNotFoundError and try again isn't helping. It's just not setting the cookie |
I'm also hitting this same issue. Restarting the auth process when the error is an instanceof CookieNotFound also caused an infinite redirect. It seems to be intermittent, it's not happening on all installs, only some. Edit: There seems to be 2 main cases where this happens
The solution I ended up going with to solve the first problem without creating an infinite loop in the second case
Second case still persists unfortunately. |
I'm using NextJS with version 6.2.0 @shopify/shopify-api. This seems like a pretty common problem that is only solved by either:
Any chance of this getting addressed in the near future? |
We were able to reproduce the issue if the shop domain is not standard (like XXXXX.myshopify.com), but the custom one. Because, yeah, redirecting to the OAuth if the the CookieNotFound error found not helping if the cookie even wasn't set |
Could you perform the following to also work around the issue?
I feel like many of us are following the documentation trying to build it out manually or update existing apps by having the shop immediately hit the /auth route then to the /auth/callback route vs first verifying if the shop is new or not. From my understanding you dont necessarily need to push a user through the /auth routes every time they enter the app otherwise it seems issues like this happen. |
@The1987 I wouldn't expect this to address the issue for us.
I'm guessing the likely fix is setting the cookie to Any chance this can get looked at by a member of the Shopify team @mkevinosullivan ? |
@zds97 - Thanks for your feedback. Again, what I was seeing was that on the first install the session did generate (using Chrome), however it wouldn't work afterwards, and thus I got the error. If I uninstalled and re-installed the app, the same thing would happen. Originally I implemented what I suggested, but then I remembered that the field isOnline allows for true or false. My app doesn't require such strict session requirements so I have set it to false, and thus far working as intended on Chrome. I haven't tested, but I wonder if having the the isOnline set to false and trying to "re-oauth" will throw the error vs having it set to true may not throw the error. Just taking a stab in the dark here. Again, I would also hypothesize that once the session has been created the browser might be storing the session (unbeknown to us) and maybe clearing your browsing data might fix this in terms of unblocking your development. |
@The1987 Thanks for the response -- it does generate and work properly most of the time, it seems to occasionally fail based on our server logs, creating a bad experience for some stores who install our app. It's not blocking development (nor can I re-produce locally on various browsesr), it's a live issue in production for a subset of customers who install our app. We utilize both online and offline auth during our install flow. (I also don't think it would have an impact as the issue seems to be a cookie thats never getting set) The only solution I've found in the near term is sending the user through a manual install path instead of using the library which requires the cookie to be present. |
Hi @zds97 & The1987 Thanks for bouncing ideas off each other. We are actively working on this. Do you have any browser information for the failures? |
@abharvey Sure. All of our instances have shops, along with the rest of the Shopify install information associated with the request, so it doesn't seem like Google crawler is causing the issue. It seems to just be missing the cookie as mentioned above. Here's an example from yesterday and the userAgent logging:
I can add additional logging if there's something specific you're looking for. |
@zds97 Thanks for getting back to us quickly. We're having difficulty reproducing the error consistently with the exception of when Chrome tries to crawl the routes. As far as logs that could help, if you could provide some debug logs before and after the error you mentioned in the issue that may help. @flamendless Could you provide any errors you're seeing in the server side logs? |
@zds97 i dont get detailed logs (idky how to configure the loggers) but heres what i get same as others: (no access to pc right now so I'll just copy-paste from the other comments) Error: Cannot complete OAuth process. Could not find an OAuth cookie for shop url: And the [shopify-api/ERROR] Could not find OAuth cookie | {shop: XXX.myshopify.com} Im using the template provided by the shopify cli using shopify/shopify-express-app |
@flamendless Thanks, since you're using the app-express template you can edit your config to add addition logging like this: // in shopify.js (unless renamed) // add import for the enum
import { LogSeverity } from "@shopify/shopify-api";
const shopify = shopifyApp({
api: {
logger: {
level: LogSeverity.Debug,
}
//... Rest of your config |
Some people might find this useful. |
I have the same issue from v5.xxx
In install process, shopify do some redirect between the app site and the shopify, and sometimes the cookie is overridden with empty value. It will make the cookie not found exception. My workaround is to override the
I don't remember why but I also create a custom SessionStorage (just save session to a db) by this issue |
@vtliem Thanks for the extra context. We're keeping an eye on this and trying some various things to better understand the underlying issue. If you're able to reproduce the issue in an arbitrary example app that you can share it would be a big help. |
hello, @abharvey any updates or steps I might be able to take? I'm running into the same error message as well but not sure how to get around it. [shopify-api/ERROR] Could not find OAuth cookie | {shop: xxxxx.myshopify.com} |
Any updates or solutions for this issue? It's an emergency because of affecting old apps upgrading to catch up with the latest API. so in case this issue couldn't be resolved soon, the app couldn't be used, affecting to revenue of the business which develops the app on Shopify. |
I've created a PR that, at least for our case, solves the issue by allowing to set an optional cookie domain (by default the library uses the domain that starts the oauth flow). |
hope this gets reviewed soon, it's going to be a huge help <3 |
Experiencing the same issue with oauth flow when im routing the requests through a few services on different domains, hopefully @cmelendez PR gets in soon! |
@wweksie for the WIN! I want to confirm that changing the samesite within the Shopify oauth package from lax to none does indeed work to resolve the issue on Chrome. |
Where are you adding this |
Anyone have an update for this case? I'm tried the I'm stuck on infinite redirect while not resolving this cookie issue |
We have the same issue - sometimes getting "Could not find OAuth cookie" error, but not consistently. I previously posted in what looks like a duplicate thread - #933 We'd REALLY appreciate an update from the Shopify/core team on this issue. Exploring workarounds in the interim. As you can see from this set of example logs, for the same shop, same setup, etc. back to back OAuth attempts. The first time fails, the second time succeeds. |
Can you guide me on How you did that, I am suffering from the same problem. |
have the same issue :((( |
Has anyone managed to find a fix? Tried this and doesn't work. Still getting same error. |
What I found is that will always get this error if the App Url and Redirect Url are different domains. Changing them to the same domain fixes the error. Secondarily, the issue could happen if the |
Just ran into this problem, does anyone have a validated way to solve this?
|
Issue summary
I am trying to demonstrate how to retrieve the Shopify store access_token using the following code, and I keep receiving this error. I reviewed another issue (#582), and it mentioned the issue should be fixed. Unfortunately, I still experience this issue.
Expected behavior
to have Shopify.auth.callback({ isOnline: true, rawRequest: req, rawResponse: res, }) to not throw an error
Actual behavior
it throw the error :
Could someone help to take a look at this issue? Thanks.
The text was updated successfully, but these errors were encountered: