README
ngx-auth
Angular authentication library based on the OpenId Connect standard (OAuth & Signed JWTs).
The major version designates the supported Angular version. For example, 6.x.x means it supports Angular 6.
Scope
Current
- OIDC compatible
- Suppurts OIDC discovery and auto configuration
- Local login via backend service
- Remote login supporting OAuth2 login
- Saving JWT in local storage
- In-memory Principal created by JWT parsing
- Parsing user tokens from URL like
/?access_token=myNiceJwt.token - Mock support for easy development handling
- Supports
HttpClientrequest interceptors for automated JWT bearer token injection - Refreshtoken support
- Silent login refresh support (iframe)
- Supports Angular 7
Future
Usage
Install via npm
npm i @elderbyte/ngx-auth --save
Import via JS/TS import statement
import {} from "@elderbyte/ngx-auth";
Configuration of AuthService (simplified)
Register module simply in your AppModule like this:
@NgModule({
...
imports: [
...
SimpleWebStorageModule.forRoot(),
ElderAuthModule.forRoot({
localLoginRoute: '/login',
accessDeniedRoute: '/access_denied',
obtainTokenUrl: environment.apiUrl + '/oauth/token',
}),
...
],
...
])
export class AppModule { }
Beware, this module depents on these two libraries:
Display DOM elements (by Role)
In case you need to hide certain elements when users do not belong to a specific role, use hasRole:
<div *hasRole="['ADMIN']">
<!-- my incredible content -->
</div>
Guarding URLs (simplified)
Besides obtaining a token from the server, of course you may want to protect routes by restricting access to authenticated users only. This is pretty straight forward:
const appRoutes: Routes = [
{
path: 'login', // Unprotected url
component: LoginComponent,
},
{
path: 'orders',
canActivate: [SimpleAuthGuard], // Only accessible for logged in users
component: OrderBrowserComponent,
}
];
- For more fine gradient role based rules, see belows
HasRoleGuard.
Guarding URLs HasRoleGuard
To prevent users from accessing parts of your application if they lack certain roles, you can use a role based router guard:
Assume you have the following route definition:
{
path: 'app/customers',
component: CustomerBrowserComponent,
}
Just add the HasRoleGuard in canActivate and specify the allowed roles under the data.roles key:
{
path: 'app/customers',
component: CustomerBrowserComponent,
canActivate: [HasRoleGuard],
data: {
roles: ['USER']
}
}
Configuration of AuthService (customized)
export class JwtAuthConfig {
/**
* Specify the OAuth client id. (Required for OAuth login)
*/
clientId?: string;
/**
* OAuth Login Page.
* If defined, the user is redirected to the OAuth login page.
*/
oAuthLoginUrl?: string;
/**
* An Angular route which is invoked on access denied.
* Defaults to '/accessdenied'
*/
accessDeniedRoute?: string;
/**
* An Angular route which is invoked on login requested
* (Used if no OAuth API is defined)
*/
localLoginRoute?: string;
/**
* Provides URL for obtaining JWT for a given grant.
*
* grant_type: password -> Get a JWT for your credentials (user & pass)
* grant_type: refresh_token -> Get a JWT for the refresh token
*
* Should be an URL like '/oauth/token'
*/
obtainTokenUrl?: string;
/**
* Provides the ability to automatically refresh JWTs
* using refresh tokens.
*
* (Requires a valid obtainTokenUrl)
*/
refresh?: {
/**
* Set the refresh strategy. (refresh disabled by default)
*/
strategy?: RefreshStrategy;
/**
* By default, the obtain-token url is used to get a refreshed JWT.
* Setting this url overwrite this default.
*/
refreshTokenUrl?: string;
/**
* The interval for the periodic strategy in milli-seconds
*/
interval?: number;
/**
* The minimal TTL before the token is refreshed in milli-seconds
*/
minTtl?: number;
};
/**
* Configure the token storage
*/
tokenStorage?: {
/**
* The storage type where to store the secrets.
*/
type?: StorageType;
/**
* The storage key name for the access_token
*/
accessTokenKeyName?: string;
/**
* The storage key name for the refresh_token
*/
refreshTokenKeyName?: string;
};
/**
* JWT Field names in accessToken used by the backend.
*/
jwt?: {
usernameField?: string;
subjectField?: string;
rolesField?: string;
issuerField?: string;
audienceField?: string;
realmField?: string;
};
/**
* JwtResponse Field names in obtaining token by the backend.
*/
jwtResponse?: {
accessTokenField?: string;
refreshTokenField?: string;
};
/**
* Mock configuration to simulate a logged in user.
*/
mock?: {
enable?: boolean;
user?: {
subject?: string;
name?: string;
roles?: string[];
issuer?: string;
audience?: string[];
realm?: string;
};
token?: {
accessToken?: string;
refreshToken?: string;
};
};
}
OAuth Sample Configuration
@NgModule({
declarations: [
imports: [
HttpClientModule,
ElderAuthModule.forRoot(
{
clientId: 'myapp',
oAuthLoginUrl: 'https://my.oauth.server/myrealm/login'
accessDeniedRoute: 'app/accessdenied',
mock: {enable: false }
}
)
]
})
export class AppModule {
}
OIDC Sample Configuration
@NgModule({
declarations: [
imports: [
HttpClientModule,
ElderAuthModule.forRoot(
{
issuerUrl: 'http://localhost:8099/auth/realms/demo',
clientId: 'demo-client',
scope: 'openid profile email',
accessDeniedRoute: 'app/accessdenied'
}
)
]
})
export class AppModule {
}
Local Login Sample Configuration
@NgModule({
declarations: [
imports: [
HttpClientModule,
ElderAuthModule.forRoot(
{
localLoginRoute: 'app/login',
accessDeniedRoute: 'app/accessdenied',
obtainTokenUrl: '/api/oauth/token',
}
)
]
})
export class AppModule {
}
Local Login Component
The LoginComponent is not included in this repository, but implementing it is easy as a pie:
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
})
export class LoginComponent {
username: string;
password: string;
constructor(private authService: AuthService) { }
login() {
this.authService
.loginWithCredentials(this.username, this.password)
.subscribe(principal => {
console.log(principal);
}, error => {
console.log(error);
});
}
}
The template may look something like this:
<div class="form-group">
<label>Username</label>
<input [(ngModel)]="username">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" [(ngModel)]="password">
</div>
<button (click)="login()">Login</button>
Development
After you have added and tested a new feature, publish a new version on npm:
- Increment the version in
package.jsonanddist/package.json- they must have the same version - Build the library into the
/distdirectory by runningnpm run buildfrom the project root directory. - Commit the compiled
/distchanges - Run
npm publish dist --access=publicfrom the project root directory ornpm publish --access=publicfrom within the dist directory.
Note --access=public is only neccessary if you publish the package the first time. If you omit this parameter the first time, you'll get a error message that you need a paid account.
License
MIT © ElderByte AG