Denied!
Securing Your Application with Better User Authorization
brian childress
i build things @ Capital One
that have highly-sensitive data
brian.childress@capitalone.com
... is the process of verifying that "you are who you say you are"...
... is the process of verifying that "you are permitted to do what you are trying to do"...
local
client
server
POST { username: password }
Status: 200
Oauth / OpenId Connect
client
server
GET /authentication
Status: 200
Auth Provider
Status: 301
GET /userInfo
Response: {
"name" : "jim halpert",
"email" : "jim@email.com"
}
Response: {
" . . ."
}
Status: 200
JSON Web Token (JWT) is an open standard ( RFC 7519 ) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
[ jot ]
eyJhbGciOiJIUzI1N9.3ODkwIiwibmFtZSI6.u6aVTx2CHvo
Header
{
"alg": "HS256",
"typ": "JWT"
}
eyJhbGciOiJIUzI1N9.3ODkwIiwibmFtZSI6.u6aVTx2CHvo
Payload
{
"sub": "1234567890",
"name": "Jim Halpert",
"email": "jim@email.com",
"iat": 1516239022,
"exp": 1782348521
}
eyJhbGciOiJIUzI1N9.3ODkwIiwibmFtZSI6.u6aVTx2CHvo
Signature
HMACSHA256(header + "." + payload,pa$sw0rd)
Payload
{
"sub": "1234567890",
"admin": true
"name": "Jim Halpert",
"email": "jim@email.com",
"iat": 1516239022,
"exp": 1516240022,
"uid: "573hdk",
"roles": [
"manager" ,
"superAdmin"
]
}
Payload
{
"sub": "1234567890",
"admin": true
"name": "Jim Halpert",
"email": "jim@email.com",
"iat": 1516239022,
"exp": 1516240022,
"uid: "573hdk",
"groups": [
6,
42,
67
]
}
client
server
GET /users
Status: 200
Authorization: Bearer { JWT }
user
role
permissions
role | canRead | canEdit | canDelete |
---|---|---|---|
user | |||
editor | |||
admin |
users can have many roles
roles can have many permissions
permissions can be assigned to many roles
user
permission
action
user | canReadProducts | canEditProducts | canDeleteProducts |
---|---|---|---|
bob | |||
sue | |||
morgan |
policy
authorization
user
IF
THEN
editor
can edit products
IF
THEN
before 9am
can read-only
Example of menu
Compare user, admin
Code example fade in
<ul>
...
<li *ngIf="role === 'admin'">
<a routerLink="/admin"
routerLinkActive="active">Admin</a>
</li>
...
<li *ngIf="admin">
<a routerLink="/users"
routerLinkActive="active">Users</a>
</li>
...
</ul>
Example of menu
Compare user, admin
Code example fade in
<ul>
...
<li *ngIf="canViewAdmin || role === 'auditor' ||
role === 'superAdmin'">
<a routerLink="/admin"
routerLinkActive="active">Admin</a>
</li>
<li>
<a routerLink="/products"
routerLinkActive="active">Products</a>
</li>
...
</ul>
C
R
U
D
reate
ead
pdate
elete
C
R
U
D
client
server
GET /routescomponents
Status: 200
Authorization: Bearer { JWT }
Response: {
"routes" : [ ... ],
"components" : [ ... ]
}
Example of menu
Compare user, admin
Code example fade in
<ul>
...
<li>
<a routerLink="/products"
routerLinkActive="active">Products</a>
</li>
<li *ngIf="getRouteAuth('users')">
<a routerLink="/users"
routerLinkActive="active">Users</a>
</li>
...
</ul>
Authorization Service
// API Response
const routeAuth = [..{
"name": "users",
"create": true,
"read": true,
"update": true,
"delete": true
}];
// Authorization Service
let getRouteAuth = (route: string) => {
// Find a matching route authorization for provided route
const auth = routeAuth.find((obj) => { return obj.name === route; });
if (auth){
return auth.read;
}
return false;
}
Authorization Service
// API Response
const routeAuth = [..{
"name": "users",
"create": true,
"read": true,
"update": true,
"delete": true
}];
// Route is the information about a specific route
canActivate(route: ActivatedRoute): boolean {
if (!this.auth.isAuthenticated() ||
!this.auth.getRouteAuth(route)) {
this.router.navigate(['login']);
return false;
}
return true;
}
route
guard
// Angular Route Guard
// API Response
const routeAuth = [..{
"name": "users",
"create": true,
"read": true,
"update": true,
"delete": true
}];
// Authorization Service
let getRouteAuth = (route: string) => {
// Find a matching route authorization for provided route
const auth = routeAuth.find((obj) => { return obj.name === route; });
if (auth){
return auth.read;
}
return false;
}
<!-- User Component -->
<form *ngIf="role === 'admin' || role === 'user'">
<div class="form-row">
<div class="form-group col-md-6">
<label for="firstName">First Name</label>
<input type="text" id="firstName" placeholder="First Name">
</div>
<div class="form-group col-md-6">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" placeholder="Last Name">
</div>
</div>
<div class="form-group">
<label for="address">Address</label>
<input type="text" id="address" placeholder="1234 Main St">
</div>
<div class="form-group">
<label for="address2">Address 2</label>
<input type="text" id="address2"
placeholder="Apartment, studio, or floor">
</div>
...
<button type="submit">Update</button>
</form>
// User Role
{
"name": "userForm",
"create": false,
"read": true,
"update": true,
"delete": false
}
<!-- User Component -->
<form *ngIf="auth.read">
<div class="form-row">
<div class="form-group col-md-6">
<label for="firstName">First Name</label>
<input type="text" id="firstName" placeholder="First Name"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
<div class="form-group col-md-6">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" placeholder="Last Name"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
</div>
<div class="form-group">
<label for="address">Address</label>
<input type="text" id="address" placeholder="1234 Main St"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
<div class="form-group">
<label for="address2">Address 2</label>
<input type="text" id="address2"
placeholder="Apartment, studio, or floor"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
...
<button type="submit" *ngIf="auth.update">Update</button>
</form>
<div *ngIf="!auth.read">
You are not authorized to view this content. Please contact your administrator.
</div>
<!-- User Component -->
<form *ngIf="auth.read">
<div class="form-row">
<div class="form-group col-md-6">
<label for="firstName">First Name</label>
<input type="text" id="firstName" placeholder="First Name"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
<div class="form-group col-md-6">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" placeholder="Last Name"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
</div>
<div class="form-group">
<label for="address">Address</label>
<input type="text" id="address" placeholder="1234 Main St"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
<div class="form-group">
<label for="address2">Address 2</label>
<input type="text" id="address2"
placeholder="Apartment, studio, or floor"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
...
<button type="submit" *ngIf="auth.update">Update</button>
</form>
<div *ngIf="!auth.read">
You are not authorized to view this content. Please contact your administrator.
</div>
<!-- User Component -->
<form *ngIf="auth.read">
<div class="form-row">
<div class="form-group col-md-6">
<label for="firstName">First Name</label>
<input type="text" id="firstName" placeholder="First Name"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
<div class="form-group col-md-6">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" placeholder="Last Name"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
</div>
<div class="form-group">
<label for="address">Address</label>
<input type="text" id="address" placeholder="1234 Main St"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
<div class="form-group">
<label for="address2">Address 2</label>
<input type="text" id="address2"
placeholder="Apartment, studio, or floor"
[disabled]="!auth.create || !auth.update" *ngIf="auth.read">
</div>
...
<button type="submit" *ngIf="auth.update">Update</button>
</form>
<div *ngIf="!auth.read">
You are not authorized to view this content. Please contact your administrator.
</div>
// Admin Role
{
"name": "userForm",
"create": true,
"read": true,
"update": true,
"delete": true
}
// Admin Role
{
"name": "userForm",
"create": true,
"read": true,
"update": true,
"delete": true
}
// Auditor Role
{
"name": "userForm",
"create": false,
"read": true,
"update": false,
"delete": false
}
// API Response
const routeAuth = [..{
"name": "userForm",
"create": true,
"read": true,
"update": true,
"delete": true
}];
@Component({
selector: 'user-form',
templateUrl: './user-form.component.html'
})
export class UserFormComponent {
...
getData(authorization: Object){
if(authorization.read){
this.http.get('users').subscribe(response => {
// do something
});
}
}
}
additional uses for CRUD-based authorization:
beta testing
system maintenance
feature rollout
a/b testing
testing
questions?
brian childress
brian.childress@capitalone.com
thank you!
bit.ly/auth_auth
slides and resources: