-
Notifications
You must be signed in to change notification settings - Fork 128
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
380 additions
and
6 deletions.
There are no files selected for viewing
90 changes: 89 additions & 1 deletion
90
AspNetCoreIdentity/ClientApp/app/components/account/account.component.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,89 @@ | ||
| ||
.table > thead > tr > th { | ||
vertical-align: bottom; | ||
border-bottom: 2px solid #ddd; | ||
white-space: nowrap; | ||
width: 1%; | ||
} | ||
|
||
.tab-content { | ||
color: #fff; | ||
} | ||
|
||
.panel-primary { | ||
background-color: #000000; | ||
background-image: linear-gradient(147deg, #000000 0%, #2c3e50 74%); | ||
} | ||
|
||
.panel-primary > .panel-heading { | ||
color: #fff; | ||
border-color: #337ab7; | ||
/*background-color: #2b4162; | ||
background-image: linear-gradient(315deg, #2b4162 0%, #12100e 74%);*/ | ||
} | ||
|
||
.panel.with-nav-tabs .panel-heading { | ||
padding: 5px 5px 0 5px; | ||
} | ||
|
||
.panel.with-nav-tabs .nav-tabs { | ||
border-bottom: none; | ||
} | ||
|
||
.panel.with-nav-tabs .nav-justified { | ||
margin-bottom: -1px; | ||
} | ||
|
||
/********************************************************************/ | ||
/*** PANEL PRIMARY ***/ | ||
.with-nav-tabs.panel-primary .nav-tabs > li > a, | ||
.with-nav-tabs.panel-primary .nav-tabs > li > a:hover, | ||
.with-nav-tabs.panel-primary .nav-tabs > li > a:focus { | ||
color: #fff; | ||
} | ||
|
||
.with-nav-tabs.panel-primary .nav-tabs > .open > a, | ||
.with-nav-tabs.panel-primary .nav-tabs > .open > a:hover, | ||
.with-nav-tabs.panel-primary .nav-tabs > .open > a:focus, | ||
.with-nav-tabs.panel-primary .nav-tabs > li > a:hover, | ||
.with-nav-tabs.panel-primary .nav-tabs > li > a:focus { | ||
color: #fff; | ||
background-color: #3071a9; | ||
border-color: transparent; | ||
} | ||
|
||
.with-nav-tabs.panel-primary .nav-tabs > li.active > a, | ||
.with-nav-tabs.panel-primary .nav-tabs > li.active > a:hover, | ||
.with-nav-tabs.panel-primary .nav-tabs > li.active > a:focus { | ||
color: #428bca; | ||
background-color: #fff; | ||
border-color: #428bca; | ||
border-bottom-color: transparent; | ||
} | ||
|
||
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu { | ||
background-color: #428bca; | ||
border-color: #3071a9; | ||
} | ||
|
||
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > li > a { | ||
color: #fff; | ||
} | ||
|
||
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > li > a:hover, | ||
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > li > a:focus { | ||
background-color: #3071a9; | ||
} | ||
|
||
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > .active > a, | ||
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > .active > a:hover, | ||
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > .active > a:focus { | ||
background-color: #4a9fe9; | ||
} | ||
|
||
|
||
#genQrCode { | ||
margin: 20px auto; | ||
padding: 20px; | ||
text-align: center; | ||
background: #ffffff; | ||
} |
91 changes: 90 additions & 1 deletion
91
AspNetCoreIdentity/ClientApp/app/components/account/account.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,90 @@ | ||
<h1>Manage account!!</h1> | ||
<div class="row"> | ||
<div class="col-md-8 col-md-offset-2"> | ||
<div class="page-header"> | ||
<h3>Account management</h3> | ||
</div> | ||
<div class="panel with-nav-tabs panel-primary"> | ||
<div class="panel-heading"> | ||
<ul class="nav nav-tabs"> | ||
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li> | ||
<li class="dropdown"> | ||
<a href="#" data-toggle="dropdown">Security <span class="caret"></span></a> | ||
<ul class="dropdown-menu" role="menu"> | ||
<li><a href="#2fa" data-toggle="tab">2 Factor Authentication</a></li> | ||
<li><a href="#resetpassword" data-toggle="tab">Reset password</a></li> | ||
</ul> | ||
</li> | ||
</ul> | ||
</div> | ||
<div class="panel-body"> | ||
<div class="tab-content"> | ||
<div class="tab-pane fade in active" id="profile"> | ||
<div class="table-responsive"> | ||
<table class="table"> | ||
<caption>Basic info</caption> | ||
<thead> | ||
<tr> | ||
<th scope="col">Username</th> | ||
<th scope="col">Email</th> | ||
<th scope="col">Phone</th> | ||
<th scope="col">2FA Enabled</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>{{accountDetails.username}}</td> | ||
<td>{{accountDetails.email}}</td> | ||
<td>{{accountDetails.phoneNumber}}</td> | ||
<td>{{accountDetails.twoFactorEnabled}}</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
<hr /> | ||
<div class="table-responsive"> | ||
<table class="table"> | ||
<caption>External providers</caption> | ||
<thead> | ||
<tr> | ||
<th scope="col">#</th> | ||
<th scope="col">Provider</th> | ||
<th scope="col"></th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr *ngFor="let provider of accountDetails.externalLogins; let i = index"> | ||
<th scope="row">{{i+1}}</th> | ||
<td>{{provider}}</td> | ||
<td> | ||
<img src="/images/{{provider}}.png" style="width: 20px; margin: 5px 5px 10px;" /> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
<div class="row tab-pane fade" id="2fa"> | ||
<div class="col-sm-6 col-sm-offset-3" style="margin-bottom:20px" *ngIf="!displayAuthenticator"> | ||
<button type="button" (click)="setupAuthenticator()" class="form-control btn">Setup authenticator</button> | ||
</div> | ||
<div class="col-sm-6 col-sm-offset-3" style="margin-bottom:20px" *ngIf="displayAuthenticator"> | ||
<p>Scan the QR Code or enter the following key into your two factor authenticator app. Spaces and casing do not matter</p> | ||
<div style="text-align: center; color: lightgreen;font-size: 20px;">{{authenticatorDetails.sharedKey}}</div> | ||
<div id="genQrCode"><span *ngIf="generatingQrCode">Generating...</span></div> | ||
<div> | ||
Once you have scanned the QR code or input the key above, your two factor authentication app will provide you with a unique code. Enter the code in the confirmation box below | ||
<input class="form-control" type="text" placeholder="Verification Code" style="margin: 10px 0;" /> | ||
<button type="button" (click)="setupAuthenticator()" class="form-control btn btn-primary">Verify</button> | ||
</div> | ||
</div> | ||
<div class="col-sm-6 col-sm-offset-3" *ngIf="accountDetails.hasAuthenticator"> | ||
<button type="button" class="form-control btn btn-danger">Reset authenticator</button> | ||
|
||
</div> | ||
</div> | ||
<div class="tab-pane fade" id="resetpassword">Default 5</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> |
57 changes: 56 additions & 1 deletion
57
AspNetCoreIdentity/ClientApp/app/components/account/account.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,69 @@ | ||
import { Component, Inject } from '@angular/core'; | ||
import { Http } from '@angular/http'; | ||
|
||
declare var QRCode: any; | ||
|
||
@Component({ | ||
selector: 'account', | ||
templateUrl: './account.component.html', | ||
styleUrls: ['./account.component.css'] | ||
}) | ||
export class AccountComponent { | ||
|
||
public accountDetails: AccountDetailsVM = <AccountDetailsVM>{}; | ||
public authenticatorDetails: AuthenticatorDetailsVM = <AuthenticatorDetailsVM>{}; | ||
public displayAuthenticator: boolean = false; | ||
public generatingQrCode: boolean = false; | ||
|
||
public generatedQRCode: any; | ||
|
||
constructor(public http: Http, @Inject('BASE_URL') public baseUrl: string) { | ||
|
||
this.http.get(this.baseUrl + 'api/manageaccount/details').subscribe(result => { | ||
this.accountDetails = result.json() as AccountDetailsVM; | ||
console.log(this.accountDetails); | ||
}, error => console.error(error)); | ||
} | ||
|
||
setupAuthenticator() { | ||
let self = this; | ||
this.http.get(this.baseUrl + 'api/manageaccount/setupAuthenticator').subscribe(result => { | ||
this.authenticatorDetails = result.json() as AuthenticatorDetailsVM; | ||
console.log(this.authenticatorDetails); | ||
this.displayAuthenticator = true; | ||
this.generatingQrCode = true; | ||
|
||
setTimeout(function () { | ||
self.generatedQRCode = new QRCode(document.getElementById("genQrCode"), | ||
{ | ||
text: self.authenticatorDetails.authenticatorUri, | ||
width: 150, | ||
height: 150, | ||
colorDark: "#000", | ||
colorLight: "#ffffff", | ||
correctLevel: QRCode.CorrectLevel.H | ||
}); | ||
self.generatingQrCode = false; | ||
(document.querySelector("#genQrCode > img") as HTMLInputElement).style.margin = "0 auto"; | ||
}, | ||
1000); | ||
|
||
}, error => console.error(error)); | ||
} | ||
} | ||
|
||
interface AccountDetailsVM { | ||
username: string; | ||
email: string; | ||
emailConfirmed: boolean; | ||
phoneNumber: string; | ||
externalLogins: string[]; | ||
twoFactorEnabled: boolean; | ||
hasAuthenticator: boolean; | ||
twoFactorClientRemembered: boolean; | ||
recoveryCodesLeft: number[]; | ||
} | ||
|
||
interface AuthenticatorDetailsVM { | ||
sharedKey: string; | ||
authenticatorUri: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
AspNetCoreIdentity/Controllers/ManageAccountController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Text.Encodings.Web; | ||
using System.Threading.Tasks; | ||
using AspNetCoreIdentity.ViewModels; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Identity; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace AspNetCoreIdentity.Controllers | ||
{ | ||
[Route("api/[controller]/[action]")] | ||
public class ManageAccountController : Controller | ||
{ | ||
private readonly UserManager<IdentityUser> _userManager; | ||
private readonly SignInManager<IdentityUser> _signInManager; | ||
private readonly UrlEncoder _urlEncoder; | ||
private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; | ||
|
||
public ManageAccountController(UserManager<IdentityUser> userManager, | ||
SignInManager<IdentityUser> signInManager, UrlEncoder urlEncoder) | ||
{ | ||
_userManager = userManager; | ||
_signInManager = signInManager; | ||
_urlEncoder = urlEncoder; | ||
} | ||
|
||
[HttpGet] | ||
[Authorize] | ||
public async Task<AccountDetailsVM> Details() | ||
{ | ||
var user = await _userManager.GetUserAsync(User); | ||
var logins = await _userManager.GetLoginsAsync(user); | ||
|
||
return new AccountDetailsVM | ||
{ | ||
Username = user.UserName, | ||
Email = user.Email, | ||
EmailConfirmed = user.EmailConfirmed, | ||
PhoneNumber = user.PhoneNumber, | ||
ExternalLogins = logins.Select(login => login.ProviderDisplayName).ToList(), | ||
TwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user), | ||
HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null, | ||
TwoFactorClientRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user), | ||
RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user) | ||
}; | ||
} | ||
|
||
[HttpGet] | ||
[Authorize] | ||
public async Task<AuthenticatorDetailsVM> SetupAuthenticator() | ||
{ | ||
var user = await _userManager.GetUserAsync(User); | ||
var authenticatorDetails = await LoadSharedKeyAndQrCodeUriAsync(user); | ||
|
||
return authenticatorDetails; | ||
} | ||
|
||
private async Task<AuthenticatorDetailsVM> LoadSharedKeyAndQrCodeUriAsync(IdentityUser user) | ||
{ | ||
// Load the authenticator key & QR code URI to display on the form | ||
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); | ||
if (string.IsNullOrEmpty(unformattedKey)) | ||
{ | ||
await _userManager.ResetAuthenticatorKeyAsync(user); | ||
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); | ||
} | ||
|
||
var email = await _userManager.GetEmailAsync(user); | ||
|
||
return new AuthenticatorDetailsVM | ||
{ | ||
SharedKey = FormatKey(unformattedKey), | ||
AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey) | ||
}; | ||
} | ||
|
||
private string FormatKey(string unformattedKey) | ||
{ | ||
var result = new StringBuilder(); | ||
int currentPosition = 0; | ||
while (currentPosition + 4 < unformattedKey.Length) | ||
{ | ||
result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); | ||
currentPosition += 4; | ||
} | ||
if (currentPosition < unformattedKey.Length) | ||
{ | ||
result.Append(unformattedKey.Substring(currentPosition)); | ||
} | ||
|
||
return result.ToString().ToLowerInvariant(); | ||
} | ||
|
||
private string GenerateQrCodeUri(string email, string unformattedKey) | ||
{ | ||
return string.Format( | ||
AuthenticatorUriFormat, | ||
_urlEncoder.Encode("ExternalAuthApp"), | ||
_urlEncoder.Encode(email), | ||
unformattedKey); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.