What are the best practices of creating an authentication system for angular 2 SPA. Is there any built-in Angular2 modules to handle this?
The core thing you need is a guard to prevent routing to pages that require authentication. Thoughtram has a great article on guards and Jason Watmore has a brilliant post showing a full authentication mechanism using a guard and JSON Web Tokens.
Whether you use JWT (and there are reasons not to) or sessions (and there are reasons not to), it all comes down to the guard.
You write a guard like this:
#Injectable()
export class AuthGuard implements CanActivate {
constructor (private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (isTheUserSignedIn()) {
return true;
}
// If not signed in, navigate to the login page and store the return URL
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
return false;
}
}
And then, wherever you configure the routes for your app module, you need to tell the route to use your guard:
const appRoutes: Routes = [
// Protect the Home route using the guard
{ path: '', component: HomeComponent, canActivate: [AuthGuard] },
// Don't protect the login and register pages
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
// Don't protect pages that don't need protecting
{ path: 'some-unprotected-page', component: UnprotectedPageComponent },
// Default redirect
{ path: '**', redirectTo: '' }
];
Related
I use GraphQL Gateway to integrate with GraphQL Federation Microservices .
but I use some Rest API code for some reason. like(refresh token , Upload Images with rest)
The Question is : how to communicate with Rest API for the other services from graphql gateway and how to send context to the controller(rest api) server.
import { IntrospectAndCompose, RemoteGraphQLDataSource } from '#apollo/gateway';
import { ApolloGatewayDriver, ApolloGatewayDriverConfig } from '#nestjs/apollo';
import { Module } from '#nestjs/common';
import { GraphQLModule } from '#nestjs/graphql';
import { AppController } from './app.controller';
#Module({
imports: [
GraphQLModule.forRoot<ApolloGatewayDriverConfig>({
driver: ApolloGatewayDriver,
server: {
// ... Apollo server options
context: ({ req, res }) => ({
authorization:req.headers.authorization,
req,
res,
url: req.protocol + '://' + req.headers.host,
}),
cors: true,
},
gateway: {
buildService({ name, url }) {
return new RemoteGraphQLDataSource({
url,
willSendRequest({ request, context }) {
request.http.headers.set('authorization',context['authorization'] );
}
});
},
supergraphSdl: new IntrospectAndCompose({
subgraphs: [
{ name: 'Service1', url: 'http://localhost:3001/graphql' },
{ name: 'Service2', url: 'http://localhost:3002/graphql' },
{ name: 'Service3' , url: 'http://localhost:3003/graphql' }
],
}),
},
}),
],controllers:[AppController]
})
export class AppModule { }
Note: if I removed '/graphql' from Url to access origin url , it gives me error[Couldn't load service definitions for service1] .
This code works fine with GraphQL but didn't work with Rest.
Server : NestJS.
Thanks..
Your question is not super clear but let me take a stab at it.
You can use a service like WunderGraph on top of your existing gateway or you can create a new gateway and ingest the GraphQL Federated Microservices. You can then introspect the REST API and ingest it into your gateway. .
I am working in a B2B Spartacus project and we are currently implementing the MyCompany User/Unit management. The Spartacus implementation is a little to complex for our use-case so we are developing a custom solution based on it.
The original implementation features a CMS-Page for users (e.g.: https://spartacus-demo.eastus.cloudapp.azure.com:444/powertools-spa/en/USD/organization/users) and then Angular child routes for the user details (e.g.: /organization/users/7a95e933-364c-4c8d-81cd-4f290df0faf1)
I tried to replicate the child route implementation following the Spartacus documentation.
I created a parent (RightsManagementUser) and child (RightsManagementUserDetails) component.
<p>rights-management-user works!</p>
<a
class="btn btn-primary"
[routerLink]="{
cxRoute: 'orgUserDetails',
params: { customerId: '9e26d9fb-14eb-4ec6-9697-3fa53302245c' }
} | cxUrl"
>Go to User Details</a
>
<router-outlet></router-outlet>
Following the Spartacus Documentation, I provided a Spartacus and an Angular routing config
export const userRoutingConfig: RoutingConfig = {
routing: {
routes: {
orgUser: {
paths: ['organization/users'],
},
orgUserDetails: {
paths: ['organization/users/:userCode'],
paramsMapping: {
userCode: 'customerId',
},
},
},
},
};
RouterModule.forChild([
{
path: null,
component: PageLayoutComponent,
canActivate: [CmsPageGuard],
data: { cxRoute: 'orgUser' },
children: [
{
path: null,
component: RightsManagementUserDetailsComponent,
data: { cxRoute: 'orgUserDetails' }
},
],
},
]),
I also tried following the documentation for Adding Angular Child Routes for a Content Page
and added the child route to the cms config.
RightsManagementUserComponent: {
component: RightsManagementUserComponent,
childRoutes: [
{
path: ':userCode',
component: RightsManagementUserDetailsComponent,
},
],
},
This all wasn't enough, when clicking the button, the CMSPageGuard tries to load the CMS page for /organization/users/7a95e933-364c-4c8d-81cd-4f290df0faf1 instead of activating the child route.
I then tried to go the Angular way and defined the child route without using cxRoute:
children: [
{
path: ':userCode',
component: PflRightsManagementUserDetailsComponent,
},
],
At first I was happy, since the child route actually activated:
But then I realized that when I do a browser refresh Spartacus again tries to access the CMS-Page instead of activating the route.
Can someone please help me out and point me to the right way to use child routes in Spartacus?
If you would like to use split view, you can define your route in this way #customizing-routes, then clone whole cms configuration for organization feature and personalize childs #customizing-cms-components.
It could looks like:
const yourConfig = { ...userCmsConfig.cmsComponents.ManageUsersListComponent };
(yourConfig.childRoutes as CmsComponentChildRoutesConfig).children[1].component = RightsManagementUserDetailsComponent;
and include in your module
imports: [
// ...
B2bStorefrontModule.withConfig({
// ...
cmsComponents: {
ManageUsersListComponent: yourConfig,
},
},
// ...
I'm learning Vue (on TS) and it seems awesome till this point. At the moment I learn about Vue routing, and have a question which I struggle to find a beautiful answer to.
Let's say, I have a parent route, named User, which gets userId as a param. I also have subpages for this route, which are called Profile and Settings respectively, and are being set in the User's children array:
routes: [
{
component: User,
name: 'User'
path: '/user/:userId',
children: [
{
component: Profile,
name: 'Profile',
path: 'profile',
},
{
component: Settings,
path: 'settings',
name: 'Settings'
},
],
}
]
It's pretty cool that I can redirect from User component to Profile or Settings as simple as
public redirectToProfile() {
this.$router.push({ name: 'Profile'});
}
But my question is - may I redirect to user's profile from outside of the User component the same way, without concatenating the path string like
public redirectToProfile() {
this.$router.push({ path: 'user/' + userId + '/profile'});
}
?
I think you can simply do it by passing params as well:
public redirectToProfile(id: string) {
this.$router.push({ name: 'Profile', params: {userId:id}});
}
I am using AWS Cognito in my application.
While doing logout i am calling the Logout Endpoint.
But after doing logout, I am still able to generate the id-tokens using the old refresh token.
It means my logout endpoint is not working any more. I am saving the tokens in my local storage, And while doing the logout i am clearing the store manually.
My Question is: How to properly use the logout mechanism of AWS
Cognito?
I'm not sure which framework you are using, but I'm using Angular. Unfortunately there are different ways of using AWS Cognito and the documentation is not clear. Here is my implementation of the Authentication Service (using Angular):
- Note 1 - With using this sign in method - once you redirect the user to the logout url - the localhost refreshes automatically and the token gets deleted.
- Note 2 - You can also do it manually by calling: this.userPool.getCurrentUser().signOut()
import { Injectable } from '#angular/core'
import { CognitoUserPool, ICognitoUserPoolData, CognitoUser } from 'amazon-cognito-identity-js'
import { CognitoAuth } from 'amazon-cognito-auth-js'
import { Router } from '#angular/router'
const COGNITO_CONFIGS: ICognitoUserPoolData = {
UserPoolId: '{INSERT YOUR USER POOL ID}',
ClientId: '{INSERT YOUR CLIENT ID}',
}
#Injectable()
export class CognitoService {
userPool: CognitoUserPool
constructor(
private router: Router
) {
this.createAuth()
}
createAuth(): void {
// Configuration for Auth instance.
const authData = {
UserPoolId: COGNITO_CONFIGS.UserPoolId,
ClientId: COGNITO_CONFIGS.ClientId,
RedirectUriSignIn : '{INSERT YOUR COGNITO REDIRECT URI}',
RedirectUriSignOut : '{INSERT YOUR COGNITO SIGNOUT URI}',
AppWebDomain : '{INSERT YOUR AMAZON COGNITO DOMAIN}',
TokenScopesArray: ['email']
}
const auth: CognitoAuth = new CognitoAuth(authData)
// Callbacks, you must declare, but can be empty.
auth.userhandler = {
onSuccess: function(result) {
},
onFailure: function(err) {
}
}
// Provide the url and parseCognitoWebResponse handles parsing it for us.
const curUrl = window.location.href
auth.parseCognitoWebResponse(curUrl)
}
/**
* Check's if the user is authenticated - used by the Guard.
*/
authenticated(): CognitoUser | null {
this.userPool = new CognitoUserPool(COGNITO_CONFIGS)
// behind the scene getCurrentUser looks for the user on the local storage.
return this.userPool.getCurrentUser()
}
logout(): void {
this.router.navigate(['/logout'])
}
}
Trying to find the answer on how to pass a common object between routes.
I have a service which store socket.io
I need to have access to that socket service across my whole site so I can listen for emits.
I have a route.ts file and I am not sure how to initialise a socket service in the root then pass it to the route when needed.
I have read the docs and I am trying to use data like below in my route.ts file:
const routes: RouterConfig = [
{ path: '', component: AppComponent },
{ path: 'function1', component: Function1Component, data: { socket: socket }},
];
However I dont know where to declare the socket service.
Thanks
In your case you are trying to pass some function reference(which is going to return object/promise/observable) from data option of route, so that would not work by passing it in data option because it does stringify the data when you ask for data by doing this.routerData.get('socket').
I strongly recommend to use resolve option of route here. resolve method would return promise/observable
Code
#Injectable()
class SocketResolver implements Resolve {
constructor(private socketService: socketService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<any> {
return this.socketService.connection();
}
}
const routes: RouterConfig = [
{ path: '', component: AppComponent },
{ path: 'function1', component: Function1Component,
resolve: { socket: SocketResolver }
}
];
Doc Link
you should not involve routing in this.
just create a service: https://angular.io/docs/ts/latest/tutorial/toh-pt4.html
SocketService.ts
import { Injectable } from '#angular/core';
#Injectable()
export class SocketService {
...
}
and inject that service in your component or other services via the constructor:
constructor(private socketService:SocketService) { }
plus you make sure your service is intialized in the bootstrap process:
bootstrap(AppComponent, [
SocketService,
... ]);
on bootstrap, angular will create a single instance of your service which will be injected into every component which has it in its constructor, therefore you have access to the same socket service from everywhere you want.