Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication Package #155

Closed
12 tasks done
ritch opened this issue Apr 17, 2017 · 5 comments
Closed
12 tasks done

Authentication Package #155

ritch opened this issue Apr 17, 2017 · 5 comments

Comments

@ritch
Copy link
Contributor

ritch commented Apr 17, 2017

Closes #136, closes #142, closes #150, closes #145, closes strongloop-internal/scrum-asteroid#164, closes strongloop-internal/scrum-asteroid#165, closes strongloop-internal/scrum-asteroid#166, closes #270, closes #271, closes #272, closes #273, closes #274, closes #275, closes #276

Support basic auth via pluggable Passport strategies

//
//  Basic psuedo implementation of the auth extension
// 

app.bind('authentication.user').toDynamicValue((
  @inject('authentication.required') required,
  @inject('http.request') req,
  @inject('authentication.strategy') strategy
) => {
  const user = await strategy.getAuthenticatedUser(req);

  if (required && !user) throw createAuthError()

  return user;
});

app.bind('authentication.required').toDynamicValue((
  @inject('controllerClass') controllerClass
  @inject('router.methodToInvoke') methodToInvoke
) => {
  return isRequired(controllerClass, methodToInvoke);
});

const authenticate = () => {
  // use Reflect.defineMeta
}
@kesavkolla
Copy link

kesavkolla commented Apr 25, 2017

Is it possible to bring in external auth and authz models? IMO integration with auth0 or keycloak etc... should be accomodated easily

@ritch
Copy link
Contributor Author

ritch commented Apr 25, 2017

What bindings do we need?

// application code (user provided)
 ctx.bind('authentication.strategy').to(() => {
    return new Strategy(verify);

    function verify(username, password, cb) {
      cb(null, {username: username});
    }
 });

// authentication package code
ctx.bind('authentication.user').to((
  @inject('authentication.strategy') strategy,
  @inject('http.request') req
) => {
   return Promise((resolve, reject) => {
     const shimReq = createShimReq(req);
     strategy.success = resolve;
     strategy.fail = () => {
       resolve();
     }
     strategy.authenticate(shimReq);
   });
});

@ritch
Copy link
Contributor Author

ritch commented Apr 25, 2017

@kesavkolla yep... once this is complete you'll be able to use Auth0Strategy from Passport.

@deepakrkris
Copy link
Contributor

since decorators for individual functions are not applicable, a slight change to the bindings will be required, like from @bind function getAuthenticatedUser() {... to export class Bindings { @bind getAuthenticatedUser() {...

@deepakrkris
Copy link
Contributor

acceptance test from Ritchie:

////////// acceptance
import {BasicStrategy} from 'passport-http';
import {authenticate, UserInfo} from '@loopback/authentication';
const app = new Application();
const server = new Server();
const client = new Client(server.url);

@api({
  basePath: '/',
  paths: {
    '/who-am-i': {
      get: {
        'x-operation-name': 'whoAmI',
        responses: {
          '200': {
            type: 'string'
          }
        }
      }
    }
  }
})
class MyController {
  constructor(public @inject('authentication.user') user : UserInfo) {

  }

  @authenticate
  public whoAmI() : string {
    const user = this.user;
    assert(user);
    return user.username;
  }
}

app.controller(MyController);
server.bind('applications.myApp').to(app);

function verifyPassword(storedPassword, providedPassword) {
  // unecrypted password example:
  return storedPassword === providedPassword;
}

const USERS = {
  joe: {username: 'joe', password: '12345'}
};

// my get user function
 app.bind('authentication.strategy').to(() => {
    return new BasicStrategy(verify);

    function verify(username, password, cb) {
      cb(null, {username: username});
    }
 });


// test the app
await server.start();
await client
  .auth({
    username: 'joe',
    password: '123456'
  })
  .get('/who-am-i'); // => joe

await client
  .auth({
    username: 'joe',
    password: 'bad password'
  })
  .get('/who-am-i'); // => 401

await client
  // no auth
  .get('/who-am-i'); // => 401

/////////

//
//  Basic psuedo implementation of the auth extension
// 

// file: loopback-next/packages/authentication/component.ts
import * as bindings from './bindings.ts';
import Component from '@loopback/component';

export class AuthenticationComponent extends Component {
  constructor() {
    this.bindings = bindings;
  }
}

// file: loopback-next/packages/authentication/bindings.ts

@bind('authentication.user')
export const getAuthenticatedUser = (
  @inject('authentication.required') required,
  @inject('http.request') req,
  @inject('authentication.strategy') strategy
) => {
  const user = await strategy.getAuthenticatedUser(req);

  if (required && !user) throw createAuthError()

  return user;
};

@bind('authentication.required')
export const isAuthenticationRequired =(
  @inject('controllerClass') controllerClass
  @inject('router.methodToInvoke') methodToInvoke
) => {
  return isRequired(controllerClass, methodToInvoke);
};

const authenticate = () => {
  // use Reflect.defineMeta
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants