amazon-archives / amazon-cognito-auth-js

The Amazon Cognito Auth SDK for JavaScript simplifies adding sign-up, sign-in with user profile functionality to web apps.
Apache License 2.0
422 stars 231 forks source link

No System.js or Angular.io support out of the box #13

Open rcfrias opened 7 years ago

rcfrias commented 7 years ago

It'd be nice to have some docs about how to use this in angular with System.Js. It is not clear to me if only with this project are we able to get a yes/no authentication, or we need to install additional packages, like the aws-sdk package.

dinvlad commented 7 years ago

FWIW it does work with Angular (4.x), though I've only tested with Webpack. I only needed to install amazon-cognito-auth-js package through NPM (without aws-sdk), and imported CognitoAuth using

import { CognitoAuth } from 'amazon-cognito-auth-js/dist/amazon-cognito-auth';

Hope that helps.

rcfrias commented 7 years ago

thkx @dinvlad , I decided to import this in index.html:

<script src="app/aws-cognito-sdk.js"></script>
<script src="app/amazon-cognito-auth.js"></script>

and use it globally. I would have preferred to have it contained inside angular as a Module, but it was taking me too long to figure out how to do it. I've seen that most modules include typings and can be installed through npm. Until amazon-cognito-auth-js gets there, I think this is the only way to go while using system.js

dinvlad commented 7 years ago

Typings are not needed here, the module already contains ES6 classes to support TypeScript out of the box afaik.

yhedaoo commented 7 years ago

Is there a working example for angular 4? I am still finding my feet with Angular atm.

rcfrias commented 7 years ago

@yhedaoo not for this project, at least not using system.js. It seems @dinvlad has tested with Webpack and I got it working with a global approach. (I am using system.js).

dinvlad commented 7 years ago

At the risk of citing a competitor solution, there's a very thorough guide from Auth0 on how to configure Angular (2+) with their library [1]. I followed it almost to the letter to configure CognitoAuth. The only difference is in how you instantiate this class, parse tokens in the callback, and get the session (arguably, it is actually easier now with CognitoAuth). Here's a rough sketch of my AuthService class, where all of CognitoAuth hides:

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

import { CognitoAuth } from 'amazon-cognito-auth-js/dist/amazon-cognito-auth';

export class AuthConfig {
  constructor(
    public readonly clientId: string,
    public readonly domain: string,
  ) {}
}

@Injectable()
export class AuthService {
  private readonly auth: CognitoAuth;

  constructor(config: AuthConfig, private readonly router: Router) {
    this.auth = new CognitoAuth({
      ClientId: config.clientId,
      AppWebDomain: config.domain,
      TokenScopesArray: [],
      RedirectUriSignIn: this.callbackUrl,
      RedirectUriSignOut: this.callbackUrl,
    });
    this.auth.userhandler = {
      onSuccess: session => this.onSuccess(session),
      onFailure: () => this.onFailure(),
    };
  }

  // have to construct callbackUrl from "scratch",
  // as Angular doesn't appear to offer a native way to do it;
  // '/login' is my callback route
  private get callbackUrl() {
    return window.location.href.split('/').slice(0, 3).concat('login').join('/');
    // or window.location.origin + '/login';
  }

  // gets called by the callback component
  login() {
    this.auth.parseCognitoWebResponse(this.router.url);
    this.auth.getSession();
  }

  // used to determine access in AuthGuard
  isAuthenticated() {
    return this.session && this.session.isValid();
  }

  private session;

  private onSuccess(session) {
    this.session = session;
    this.router.navigateByUrl(this.guardedUrl);
  }

  private onFailure() {
    this.session = undefined;
  }

  get accessToken() {
    return this.session && this.session.getAccessToken().getJwtToken();
  }

  // save and restore initial route upon redirect from Cognito-hosted page

  set guardedUrl(url: string) {
    localStorage.setItem('guardedUrl', url);
  }

  get guardedUrl() {
    return localStorage.getItem('guardedUrl') || '/';
  }
}

Everything else may be done exactly the same as in [1].

[1] https://auth0.com/docs/quickstart/spa/angular2/01-login

yhedaoo commented 7 years ago

image

the redirect back to localhost:4200/login seems to fail when I try it.. do you have a boilerplate angular4 application set up that I can possibly look at? Thanks

rcfrias commented 7 years ago

@yhedaoo I had the same problem, please look at this recommendations:

  1. Cognito Auth only works with HTTPS, so your localhost must be configured for this secure access.
  2. Make sure you enable both "code" and "implicit" authentication methods in the Cognito application settings.
  3. Double check your redirect urls, they must all match client & server side.
  4. Make a screenshot of the error selecting "login" instead of "favicon.ico" Could provide more insight. Good luck!
dinvlad commented 7 years ago

@yhedaoo yes, everything @rcfrias said needs to fall into place (although "code" method is not needed for Angular, unless it's server-side rendered). You do need to configure a /login route for redirect, or alternatively redirect to the base of your app but then call the login() method from ngOnit of your AppComponent. Unfortunately I'm still in the process of open-sourcing my application, but once it's ready I can share a link.

Btw if it's just an https error then you can easily enable ssl from ng serve with --ssl --ssl-key path/to/your/self-signed.key --ssl-cert path/to/your/self-signed.cert. The last 2 you generate with openssl command line.

yuntuowang commented 6 years ago

Does any of the suggestions work? @yhedaoo

desalegnbiru commented 6 years ago

I get this error on Ubuntu using angular

Cannot find module 'amazon-cognito-auth-js/dist/amazon-cognito-auth'.

iamcootis commented 6 years ago

Hi @dinvlad, could you help me understand a few things from your example?

I am able to go to the AWS created page and log in using facebook or google. I am returned a access_token in my callback, but I am not sure how I am supposed to capture this token? My callback is localhost:4200/callback, but as soon as my application goes to this route, it re-routes back to localhost:4200 and removes the hash.

Neither the onSuccess nor the onFailure functions are fired in my authService. Sorry if this is obvious stuff. I am fairly new to Angular.

dinvlad commented 6 years ago

@iamcootis sorry, not sure how to help. Generally, your Angular Router should be set up to direct your callback route to the Auth component that will invoke authService.login() when hit.

Btw to anyone concerned, unfortunately I'm no longer using this library (we moved to Firebase). I'd defer to Amazon staff here for further help.

yuntuowang commented 6 years ago

Hi @iamcootis, You need to call "auth.parseCognitoWebResponse(curUrl); ", which would have extracted the tokens from the url and invoked onSuccess()/onError() callbacks.

You can see the example in our sample line 97 to 112. https://github.com/aws/amazon-cognito-auth-js/blob/master/sample/index.html Here you can see that you need to initialize auth object first(line 194 to 216), then call "auth.getSession()" for sign-in, then call "auth.parseCognitoWebResponse(curUrl);" to parse the callback url containing tokens or code.

marc101101 commented 6 years ago

@yuntuowang The problem, and I think @dinvlad is having the same issue, is that your userhandler is not working in angular.

this.auth.userhandler = {
      onSuccess: session => this.onSuccess(session),
      onFailure: () => this.onFailure(),
    };

This part of the code never gets triggered. Do you have a solution for that? Or an working angular example?

ritesh-nitw commented 6 years ago

@marc101101 : Yes indeed. Same problem, userhandler is not working in angular, onSuccess and onFailure never being called

marc101101 commented 6 years ago

Fixed this problem. You have to init the handler bevore calling: this.auth.parseCognitoWebResponse(this.router.url);

PsiOps commented 6 years ago

I still struggled to get the handlers firing. What made it click for me was the following: The Component that will be loaded by the Router when the RedirectUriSignIn url is activated should call the this.auth.parseCognitoWebResponse(this.router.url) method, eg in the ngOnInit method. That url will have the token/code added in the query string, and parsing that will trigger the onSuccess handler, which in turn will give you your session object. So while anything can call the this.auth.getSession() to go to the AWS Cognito hosted web ui page, the callback Component (the Component linked by the Router to the RedirectUriSignIn url you have configured) should parse the url to get the session. Hope this helps someone.