aws-samples / amazon-cognito-passwordless-auth

Passwordless authentication with Amazon Cognito: FIDO2 (WebAuthn, support for Passkeys), Magic Link, SMS OTP Step Up
Apache License 2.0
370 stars 65 forks source link

Documentation: Usage in (plain) web #1

Open Vlaaaaaaad opened 1 year ago

Vlaaaaaaad commented 1 year ago

Hi,

Awesome sample code y'all built here ❣️ Thank you!

As somebody that's considering using Cognito in a plain/vanilla-JavaScript project, I'd really appreciate the still-to-be-written docs. I am creating this issue to show/track demand 🙂

https://github.com/aws-samples/amazon-cognito-passwordless-auth/blob/156f9233cbee9ff1b3519e9b1308da08b2119ab0/client/README.md?plain=1#L3

ottokruse commented 1 year ago

Thanks for your interest! Yes we should really document this.

Here a super quick braindump to get you started, might you be so brave:

--

This will be the same in plain web:

import { Passwordless } from "amazon-cognito-passwordless-auth";

Passwordless.configure({
  cognitoIdpEndpoint:
    "<AWS region where the CDK stack was deployed to, e.g. eu-west-1>",
  clientId:
    "<Cognito User Pool Client ID, one of the outputs of the CDK stack>",
  fido2: {
    baseUrl:
      "<The base URL to the FIDO2 API, one of the outputs of the CDK stack>",
  },
  debug: console.debug, // Optional: adds logging
});

And then you can import from the respective files, e.g. for magic link:

import { requestSignInLink, signInWithLink } from "amazon-cognito-passwordless-auth/magic-link";

// somewhere in your web app, e.g. in a button on-click handler, you request the magic link:
const { signInLinkRequested } = requestSignInLink({ username: "alice" });
try {
  await signInLinkRequested;
  console.log("We've emailed you a secret sign-in link")
} catch(err) {
  console.log("Oops", err)
}

// on load of your web app, you catch the magic link
// (this is a no-op if there is no magic link in the browser URL bar)
const { signedIn } = signInWithLink({
  statusCb: console.log, // will log e.g. SIGNED_IN_WITH_LINK, NO_SIGNIN_LINK, SIGNIN_LINK_EXPIRED, ...
});
await signedIn; // This promise resolves once sign-in completes (or if it was a no-op)

The status logged by the statusCb above is the status of the sign in process, i.e. the process of signing in with magic link in this case.

The signedIn promise resolves also if there's nothing to do (e.g. because there's no sign in link in the URL). So it doesn't necessarily mean that the user is signed in after this promise resolves.

To determine the active sign-in status, i.e. is the user currently signed in or not, you should simply check the JWTs in storage. If you have valid JWTs you are signed in. For ease you can just check the expireAt field which should cover it, but you could also actually verify the JWTs:

import { retrieveTokens } from "amazon-cognito-passwordless-auth/storage";

const tokens = await retrieveTokens();
if (tokens && tokens.expireAt > new Date()) {
  console.log("Looks like the user is signed in");
}

See also the React code, which works just like that, but covers more nuances: https://github.com/aws-samples/amazon-cognito-passwordless-auth/blob/cddc481c68bcd4175d835d035d6880046607bb58/client/react/hooks.tsx#L319-L338

HasseJohansen commented 1 year ago

Hi

Thank you for building this

I am also trying to use this from plain javascript(I am not experienced writting javascript and I don't know react)

I do get a successful login with a magic link following your example (after changing username to usernameOrAlias)

But how do I track the logged in state? it doesn't seem like signInWithLink() (and by that signedIn) is tracking that and as I can see a comment in the React code telling that signInWithLink without a magic link is a no-op will it ever then reach catch in this example? Because having:

const { signedIn } = signInWithLink();
try {
  await signedIn;
  console.log("You made it in!")
} catch(err) {
  console.log("Oops", err)
}

Will just forever hit the console.log("You made it in!")

And that even after I delete all the tokens in local storage

In the React code it seems like you pass a statusCb and tokensCb. Can I do something similar?

ottokruse commented 1 year ago

You're right you need to use the statusCb for that. I updated my example to show. Let me know if that helps.

menkari commented 1 month ago

Has anyone here taken a stab at porting hooks to an appropriate angular service? I'm looking to port this across with some of the niceties of the scheduling token refresh etc