vocably / pontis

Painless Chrome extension authentication via AWS Amplify websites
MIT License
7 stars 0 forks source link

Almost works #3

Open henrikra opened 1 year ago

henrikra commented 1 year ago

I have setup extension and web site where I do the authentication like this library is saying. After login with my website the extension returns user when calling cognitoUserPool.getCurrentUser() which is correct. But the the returned user object's session is null. This means I cannot call for example currentUser.getSession(). Calling that function works in the website but not on the extensions service worker.

Are you able to say if I am missing something or not?

sneas commented 1 year ago

Hey @henrikra, please don't be mad at me, you're, most likely, the second user of this library. And I'm the first one.

I'm not very experienced with Ampliy, so I might be doing something wrong, but this is how I get user session:

import { Auth } from '@aws-amplify/auth';

(async () => {
  const currentUser = await Auth.currentAuthenticatedUser();
  if (currentUser === false) {
    return;
  }

  const session = await Auth.userSession(currentUser);
})();
henrikra commented 1 year ago

Thanks for the quick reply! And I am not mad :D

I am using amazon-cognito-identity-js which is AWS alternative for using Cognito in frontend.

It is used like this. Can you also try if it works for you or not?

import { CognitoUserPool } from "amazon-cognito-identity-js";

function main() {
  const cognitoUserPool = new CognitoUserPool({
    UserPoolId: "id_here",
    ClientId: "id_here",
  });

  const currentUser = cognitoUserPool.getCurrentUser()

  if (currentUser === null) {
    return
  }

  // With Pontis currentUser.getSession is undefined
  currentUser.getSession((error, session) => {
    console.log('session', session)
  });
}

main();
sneas commented 1 year ago

I've just tried it out with amazon-cognito-identity-js and it seems working. Do you pas Storage param to CognitoUserPool? It should look something like:

// web app
import { CognitoUserPool } from "amazon-cognito-identity-js";
import { AppAuthStorage } from '@vocably/pontis';

const cognitoUserPool = new CognitoUserPool({
  UserPoolId: "id_here",
  ClientId: "id_here",
  Storage: new AppAuthStorage(chromeExtensionId),
});
// extension
import { CognitoUserPool } from "amazon-cognito-identity-js";
import { registerExtensionStorage } from '@vocably/pontis';

const cognitoUserPool = new CognitoUserPool({
  UserPoolId: "id_here",
  ClientId: "id_here",
  Storage: registerExtensionStorage('sync'),
});
henrikra commented 1 year ago

Yes I am but it is not working. Interesting 🤔

Would it be possible if you could create a new repo that has example web app and extension setup that works with Pontis? That would be beneficial to users of this library to see what kind of setup works 👍 You could link the example repo to the README of Pontis

sneas commented 1 year ago

I'm sorry, I was wrong. I can't make my tests work with amazon-cognito-identity-js. Seems like the reason is amazon-cognito-identity-js doesn't call for the sync function. At least I can't find such a call in the code of amazon-cognito-identity-js.

It seems like to make auth work on both app and extension, you'll have to call the sync function manually. I'm not sure if this is something you want, though.

// web app
import { CognitoUserPool } from "amazon-cognito-identity-js";
import { AppAuthStorage } from '@vocably/pontis';

const storage = new AppAuthStorage(chromeExtensionId);

storage.sync().then(() => {
  const cognitoUserPool = new CognitoUserPool({
    UserPoolId: "id_here",
    ClientId: "id_here",
    Storage: storage,
  });
});
// extension
import { CognitoUserPool } from "amazon-cognito-identity-js";
import { registerExtensionStorage } from '@vocably/pontis';

const storage = registerExtensionStorage('sync');
storage.sync().then(() => {
  const cognitoUserPool = new CognitoUserPool({
    UserPoolId: "id_here",
    ClientId: "id_here",
    Storage: storage,
  });
});

Or use @aws-amplify/auth instead.

sneas commented 1 year ago

Given the fact, React Native also uses async storage, the React Native example in the doc looks quite similar:

var poolData = {
    UserPoolId: '...', // Your user pool id here
    ClientId: '...', // Your client id here
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);

userPool.storage.sync(function(err, result) {
    if (err) {
    } else if (result === 'SUCCESS') {
        var cognitoUser = userPool.getCurrentUser();
        // Continue with steps in Use case 16
    }
});
henrikra commented 1 year ago

Thanks for the suggestion.

Now I tried the trick to call sync before I call getCurrentUser in my extension. Still it does not work. I made sure to call storage.sync() before calling getCurrentUser but still currentUser.getSession is not a function in the extension the same way I described in the issue. currentUser.getSession works fine on the web page side.

Are you able to make small example repo so I can see if I am doing it correctly with amazon-cognito-identity-js or not? This way we are on the same page 👍

sneas commented 1 year ago

@henrikra, I'm very very sorry it isn't working for you and I'd wish to dive deeper into the problem of why this library isn't working with amazon-cognito-identity-js, but I have other priorities on my plate, thus I have no idea when I can provide a usage example. As far as I understand from this thread, you're doing pretty much the same I would have done with the pontis + amazon-cognito-identity-js. You, probably, already have some code with pontis + amazon-cognito-identity-js, I'd encourage you to put it on GitHub so we can have a look at it together and, hopefully, figure something out.

henrikra commented 1 year ago

I can try to make minimum reproduction repo for you 👍

Can you fix this more easier issue in the meanwhile? https://github.com/vocably/pontis/issues/2

bodokaiser commented 7 months ago

@henrikra did you find a solution to this problem?

bodokaiser commented 7 months ago

Is it correct that the following code must run as content script on your web app?

// website-app/index.js

import { Auth } from '@aws-amplify/auth';
import { AppAuthStorage } from '@vocably/pontis';

const extensionId = 'baocigmmhhdemijfjnjdidbkfgpgogmb';

Auth.configure({
  // The following line sets up the custom storage
  // which exchages auth tokens with the extension
  storage: new AppAuthStorage(extensionId),
  // and the rest of Auth params:
  region: 'eu-central-1',
  userPoolId: 'eu-central-1_uSErPooL',
  //etc...
});

Otherwise, window.localStorage, which is used by the StorageHelper from @aws-amplify/core, which in turn is used by this library, is not the same as the website, and therefore does not contain the cognito credentials, correct?

In this case, if one wants to access the authenticated user. e.g., to perform GraphQL requests, in the background worker, the popup script, or a content script from a different website, one would have to synchronize the Cognito credentials via the background worker?

sneas commented 7 months ago

@bodokaiser , the // website-app/index.js snippet must be run on the website of your extension. Not the content script. The authentication happens on the website, and the auth data will be passed to the extension and stored in the background service worker. That's why, having proper extensionId is crucial for things to work.

bodokaiser commented 7 months ago

@sneas I confused extension-operations.ts with extension-storage-operations.ts but it is in extension-operations where you use your @vocably/hermes library to send messages via chrome.runtime.sendMessage to the chrome extension which in turn uses chrome.runtime.onMessageExternal.

This insight allowed me to check if the messages are delivered by running chrome.runtime.sendMessage("<chrome extension id>", { foo: "bar" }) in the debugger console of the website and adding chrome.runtime.onMessageExternal.addListener(console.info) to the background service worker.

The result was that actually, chrome.runtime was not available on my website because I haven't added it to externally_connectable, e.g.,

{
  "manifest_version": 3,
  // ...
  "permissions": ["storage"],
  "host_permissions": ["http://localhost:3000/*"],
  "externally_connectable": {
    "matches": ["http://localhost:3000/*"]
  }
}
sneas commented 7 months ago

Thank you for sharing your thought process and your discovery, @bodokaiser. The installation was quite obvious to me, but apparently, not very transparent to you, and most likely, other users. I will update the library documentation to make the installation process more straightforward.

noma6386-duplicate commented 5 months ago

I am not sure the meaning of the website of your extension. the // website-app/index.js snippet must be run on the website of your extension. Not the content script. It means I need to put the code in my web site ? So, then, If I publish an extension more than one app stores, it have more than two extensionId. What do I should do ?

sneas commented 5 months ago

Hi @noma6386-duplicate, you are right. The website/script.ts should be run on the website. Not on the content script. You redirect users to your website, authenticate them, and the lib passes token to the extension. Your website has to know the ID of the extension. This is how I solved this problem for Chrome and Safari extensions: https://github.com/vocably/vocably-pro/blob/main/packages/app/src/extension-id.ts