Skip to content

Commit c5cd96b

Browse files
committed
create security/authentication component
1 parent b557161 commit c5cd96b

File tree

6 files changed

+246
-1
lines changed

6 files changed

+246
-1
lines changed

src/app/security/authentication/authentication.component.css

Whitespace-only changes.
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<div class="container pt-3">
2+
<div class="row">
3+
<div id="sidebar" style="width: 260px;">
4+
<app-sidebar></app-sidebar>
5+
</div>
6+
<div id="content" class="flex-grow-1" style="width: 400px;">
7+
<article>
8+
<h1>Authentication</h1>
9+
<hr>
10+
<p>
11+
Authentication is the process of verifying the identity of the user who accesses a web application. This may require the user to input a username and password to log into the web application.
12+
</p>
13+
<p>
14+
Authentication is handled by the authentication middleware, which uses the registered authentication service that can have multiple authentication schemes, and each scheme is related to an authentication handler to determine later in the authorization policies which authentication handler should be used to authenticate the user and provides a <code>ClaimsIdentity</code> that represents the user in the request context.
15+
</p>
16+
<h3>Configuration</h3>
17+
<p>
18+
The authentication service is added to the application by calling the <code>addAuthentication()</code> method, and then authentication schemes can be specified by calling one of the following methods:
19+
</p>
20+
<ul>
21+
<li><code>AddCookie()</code> for cookie-based authentication</li>
22+
<li><code>AddJwtBearer()</code> for token-based authentication</li>
23+
</ul>
24+
<p>
25+
The authentication middleware is used in the application by calling the <code>useAuthentication()</code> method and must be called before any middleware that depends on the user being authenticated.
26+
</p>
27+
<p>
28+
This example shows the configuration of both approaches, cookie-based and token-based authentication, but you can choose one of the two approaches.
29+
</p>
30+
<pre><code class="language-php">&lt;?php
31+
32+
namespace Application;
33+
34+
use DevNet\System\TimeSpan;
35+
use DevNet\Web\Hosting\WebHost;
36+
use DevNet\Web\Extensions\ApplicationBuilderExtensions;
37+
use DevNet\Web\Extensions\ServiceCollectionExtensions;
38+
39+
class Program
40+
&lcub;
41+
public static function main(array $args = [])
42+
&lcub;
43+
$builder = WebHost::createDefaultBuilder($args);
44+
$builder->register(function ($services) &lcub;
45+
// Adding authentication service
46+
$services->addAuthentication(function ($builder) &lcub;
47+
// Adding cookie-based authentication handler
48+
$builder->addCookie(AuthenticationScheme::CookieSession, function ($options) &lcub;
49+
// Optimally you can modify the following default options.
50+
$options->CookieName = "Identity";
51+
$options->CookiePath = "/";
52+
$options->ExpireTime = TimeSpan::fromDays(7);
53+
});
54+
// Adding token-based authentication handler
55+
$builder->addJwtBearer(AuthenticationScheme::JwtBearer, function ($options) &lcub;
56+
$options->SecurityKey = "jwt security key";
57+
// Optinally you can configure the following validation options
58+
$options->Issuer = "127.0.0.1:8000"; // server address
59+
$options->Audience = "127.0.0.1:8080"; // client address
60+
});
61+
});
62+
});
63+
64+
$host = $builder->build();
65+
66+
$host->start(function ($app) &lcub;
67+
$app->UseExceptionHandler();
68+
$app->useRouter();
69+
// Adding the authentication middleware befor the endpoint middleware.
70+
$app->useAuthentication();
71+
$app->useEndpoint(function ($routes) &lcub;
72+
// routes
73+
});
74+
});
75+
}
76+
}
77+
</code></pre>
78+
<br>
79+
<h3>Cookie-based Authentication</h3>
80+
<p>
81+
The cookie-based authentication is a stateful process, which means that the server stores the user session data and sends to the client a cookie that contains a session reference, which is often stored in the browser and sent back to the server with every request to authenticate the client requests and maintain session information on the server over the stateless HTTP protocol.
82+
</p>
83+
<p>
84+
The following example demonstrates the working process of cookie-based authentication using <code>ClaimsIdentity</code> and the <code>Authentication</code> service to log in and log out the user.
85+
</p>
86+
<pre><code class="language-php">&lt;?php
87+
88+
use DevNet\System\Linq;
89+
use DevNet\System\Collections\ArrayList;
90+
use DevNet\Web\Http\HttpContext;
91+
use DevNet\Web\Security\Authentication\AuthenticationScheme;
92+
use DevNet\Web\Security\ClaimsIdentity;
93+
use DevNet\Web\Security\ClaimsType;
94+
...
95+
96+
$app->useEndpoint(function($routes) &lcub;
97+
$routes->mapPost("/login", function(HttpContext $context) &lcub;
98+
$user = $context->User;
99+
if (!$user->isAuthenticated()) &lcub;
100+
$form = $context->Resquest->Form;
101+
$json = file_get_contents(__DIR__ . '/path/to/data.json');
102+
$data = json_decode($json, true);
103+
$users = new ArrayList('object');
104+
$users->addRange($data);
105+
106+
$user = $users->where(fn ($user) => $user->Username == $form->getValue('username'))->first();
107+
if (!$user || $user->Password != $form->getValue('password')) &lcub;
108+
return $context->Response->setStatusCode(401);
109+
}
110+
111+
$identity = new ClaimsIdentity(AuthenticationScheme::CookieSession);
112+
$identity->addClaim(new Claim(ClaimType::Identifier , $form->getValue('username')));
113+
$identity->addClaim(new Claim(ClaimType::Role, 'member'));
114+
115+
$context->Authentication->signIn($identity, $form->getValue('remember'));
116+
}
117+
118+
return $context->Response->redirect('/account');
119+
});
120+
121+
$routes->mapGet("/logout", function(HttpContext $context) &lcub;
122+
$context->Authentication->signOut();
123+
return $context->Response->redirect('/login');
124+
})
125+
});</code></pre>
126+
<br>
127+
<h3>Token-based Authentication</h3>
128+
<p>
129+
The token-based authentication is a stateless process. This means that the server does not store any session information about the user on its side. Instead, it sends to the client an encrypted token, typically JWT (JSON Web Token), that contains the user information and expiration time, and the client stores this token and sends it back to the server with every request, where the server does the token validation and grants access to the user.
130+
</p>
131+
<p>
132+
The following example demonstrates the working process of token-based authentication using ClaimsIdentity and JwtSecurityTokenHandler to generate a JWT token and send it to the client to send it back later for authentication.
133+
</p>
134+
<p>
135+
Due to the limitations of this approach, there is no option for remembering or logging out the user on the server side. However, you can easily log out by removing the token from your request header on the client side.
136+
</p>
137+
<pre><code class="language-php">&lt;?php
138+
139+
use DevNet\System\Linq;
140+
use DevNet\System\Collections\ArrayList;
141+
use DevNet\Web\Http\HttpContext;
142+
use DevNet\Web\Security\Authentication\AuthenticationScheme;
143+
use DevNet\Web\Security\ClaimsIdentity;
144+
use DevNet\Web\Security\ClaimsType;
145+
...
146+
147+
$app->useEndpoint(function($routes) &lcub;
148+
$routes->mapGet("/login", (function(HttpContext $context) &lcub;
149+
$user = $context->User;
150+
if (!$user->isAuthenticated()) &lcub;
151+
$form = $context->Resquest->Form;
152+
$json = file_get_contents(__DIR__ . '/path/to/data.json');
153+
$data = json_decode($json, true);
154+
$users = new ArrayList('object');
155+
$users->addRange($data);
156+
157+
$user = $users->where(fn ($user) => $user->Username == $form->getValue('username'))->first();
158+
if (!$user || $user->Password != $form->getValue('password')) &lcub;
159+
return $context->Response->setStatusCode(401);
160+
}
161+
162+
$claims = new ClaimsIdentity(AuthenticationScheme::JwtBearer);
163+
$claims->addClaim(new Claim('sub', $form->getValue('username')));
164+
$claims->addClaim(new Claim('role', 'member'));
165+
$claims->addClaim(new Claim('iss', '127.0.0.1:8000'));
166+
$claims->addClaim(new Claim('aud', '127.0.0.1:8080'));
167+
168+
// The asymmetric enciption RSA not supported yet, only the symmetric encription HSA for now.
169+
$token = new JwtSecurityToken($claims, 'HS256', new DateTime('7 days'));
170+
$jwtHandler = new JwtSecurityTokenHandler();
171+
$signedToken = $jwtHandler->writeToken($token, "jwt security key");
172+
173+
// Need to send the token to the client.
174+
return $context->Response->WriteJsonAsync(['jwt' => $signedToken]);
175+
}
176+
177+
return $context->Response->redirect('/account');
178+
}));
179+
});</code></pre>
180+
181+
<blockquote class="alert alert-warning">
182+
<b>Important:</b> The client should send back the JWT token in the Authorization header using the Bearer schema in the following format: <code>Authorization: Bearer &lt;token></code>
183+
</blockquote>
184+
</article>
185+
<nav class="no-print" aria-label="Page navigation">
186+
<ul class="nav-page">
187+
<li class="nav-page-item">
188+
<a class="nav-page-link" routerLink="/docs/security/overview">
189+
<i class="chevron left"></i> Previous
190+
</a>
191+
</li>
192+
<li class="nav-page-item">
193+
<a class="nav-page-link" routerLink="/docs/security/authorisation">
194+
Next <i class="chevron right"></i>
195+
</a>
196+
</li>
197+
</ul>
198+
</nav>
199+
</div>
200+
</div>
201+
</div>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { AuthenticationComponent } from './authentication.component';
4+
5+
describe('AuthenticationComponent', () => {
6+
let component: AuthenticationComponent;
7+
let fixture: ComponentFixture<AuthenticationComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
declarations: [ AuthenticationComponent ]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(AuthenticationComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import hljs from 'highlight.js/lib/common';
3+
4+
@Component({
5+
selector: 'security-authentication',
6+
templateUrl: './authentication.component.html',
7+
styleUrls: ['./authentication.component.css']
8+
})
9+
export class AuthenticationComponent implements OnInit {
10+
11+
constructor() { }
12+
13+
ngOnInit(): void {
14+
hljs.highlightAll();
15+
}
16+
17+
}

src/app/security/security-routing.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { NgModule } from '@angular/core';
22
import { RouterModule, Routes } from '@angular/router';
33
import { OverviewComponent } from './overview/overview.component';
4+
import { AuthenticationComponent } from './authentication/authentication.component';
45

56
const routes: Routes = [
67
{ path: 'docs/security/overview', component: OverviewComponent },
8+
{ path: 'docs/security/authentication', component: AuthenticationComponent },
79
{ path: 'docs/security', redirectTo: 'docs/security/overview', pathMatch: 'full' }
810
];
911

src/app/security/security.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { CommonModule } from '@angular/common';
33
import { SecurityRoutingModule } from './security-routing.module';
44
import { SharedModule } from '../shared/shared.module';
55
import { OverviewComponent } from './overview/overview.component';
6+
import { AuthenticationComponent } from './authentication/authentication.component';
67

78
@NgModule({
89
declarations: [
9-
OverviewComponent
10+
OverviewComponent,
11+
AuthenticationComponent
1012
],
1113
imports: [
1214
CommonModule,

0 commit comments

Comments
 (0)