Skip to content

Commit 080c5cf

Browse files
committed
generate qrcode
1 parent 1308134 commit 080c5cf

File tree

10 files changed

+380
-6
lines changed

10 files changed

+380
-6
lines changed
Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,89 @@
1-

1+
.table > thead > tr > th {
2+
vertical-align: bottom;
3+
border-bottom: 2px solid #ddd;
4+
white-space: nowrap;
5+
width: 1%;
6+
}
7+
8+
.tab-content {
9+
color: #fff;
10+
}
11+
12+
.panel-primary {
13+
background-color: #000000;
14+
background-image: linear-gradient(147deg, #000000 0%, #2c3e50 74%);
15+
}
16+
17+
.panel-primary > .panel-heading {
18+
color: #fff;
19+
border-color: #337ab7;
20+
/*background-color: #2b4162;
21+
background-image: linear-gradient(315deg, #2b4162 0%, #12100e 74%);*/
22+
}
23+
24+
.panel.with-nav-tabs .panel-heading {
25+
padding: 5px 5px 0 5px;
26+
}
27+
28+
.panel.with-nav-tabs .nav-tabs {
29+
border-bottom: none;
30+
}
31+
32+
.panel.with-nav-tabs .nav-justified {
33+
margin-bottom: -1px;
34+
}
35+
36+
/********************************************************************/
37+
/*** PANEL PRIMARY ***/
38+
.with-nav-tabs.panel-primary .nav-tabs > li > a,
39+
.with-nav-tabs.panel-primary .nav-tabs > li > a:hover,
40+
.with-nav-tabs.panel-primary .nav-tabs > li > a:focus {
41+
color: #fff;
42+
}
43+
44+
.with-nav-tabs.panel-primary .nav-tabs > .open > a,
45+
.with-nav-tabs.panel-primary .nav-tabs > .open > a:hover,
46+
.with-nav-tabs.panel-primary .nav-tabs > .open > a:focus,
47+
.with-nav-tabs.panel-primary .nav-tabs > li > a:hover,
48+
.with-nav-tabs.panel-primary .nav-tabs > li > a:focus {
49+
color: #fff;
50+
background-color: #3071a9;
51+
border-color: transparent;
52+
}
53+
54+
.with-nav-tabs.panel-primary .nav-tabs > li.active > a,
55+
.with-nav-tabs.panel-primary .nav-tabs > li.active > a:hover,
56+
.with-nav-tabs.panel-primary .nav-tabs > li.active > a:focus {
57+
color: #428bca;
58+
background-color: #fff;
59+
border-color: #428bca;
60+
border-bottom-color: transparent;
61+
}
62+
63+
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu {
64+
background-color: #428bca;
65+
border-color: #3071a9;
66+
}
67+
68+
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > li > a {
69+
color: #fff;
70+
}
71+
72+
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > li > a:hover,
73+
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > li > a:focus {
74+
background-color: #3071a9;
75+
}
76+
77+
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > .active > a,
78+
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > .active > a:hover,
79+
.with-nav-tabs.panel-primary .nav-tabs > li.dropdown .dropdown-menu > .active > a:focus {
80+
background-color: #4a9fe9;
81+
}
82+
83+
84+
#genQrCode {
85+
margin: 20px auto;
86+
padding: 20px;
87+
text-align: center;
88+
background: #ffffff;
89+
}
Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,90 @@
1-
<h1>Manage account!!</h1>
1+
<div class="row">
2+
<div class="col-md-8 col-md-offset-2">
3+
<div class="page-header">
4+
<h3>Account management</h3>
5+
</div>
6+
<div class="panel with-nav-tabs panel-primary">
7+
<div class="panel-heading">
8+
<ul class="nav nav-tabs">
9+
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
10+
<li class="dropdown">
11+
<a href="#" data-toggle="dropdown">Security <span class="caret"></span></a>
12+
<ul class="dropdown-menu" role="menu">
13+
<li><a href="#2fa" data-toggle="tab">2 Factor Authentication</a></li>
14+
<li><a href="#resetpassword" data-toggle="tab">Reset password</a></li>
15+
</ul>
16+
</li>
17+
</ul>
18+
</div>
19+
<div class="panel-body">
20+
<div class="tab-content">
21+
<div class="tab-pane fade in active" id="profile">
22+
<div class="table-responsive">
23+
<table class="table">
24+
<caption>Basic info</caption>
25+
<thead>
26+
<tr>
27+
<th scope="col">Username</th>
28+
<th scope="col">Email</th>
29+
<th scope="col">Phone</th>
30+
<th scope="col">2FA Enabled</th>
31+
</tr>
32+
</thead>
33+
<tbody>
34+
<tr>
35+
<td>{{accountDetails.username}}</td>
36+
<td>{{accountDetails.email}}</td>
37+
<td>{{accountDetails.phoneNumber}}</td>
38+
<td>{{accountDetails.twoFactorEnabled}}</td>
39+
</tr>
40+
</tbody>
41+
</table>
42+
</div>
43+
<hr />
44+
<div class="table-responsive">
45+
<table class="table">
46+
<caption>External providers</caption>
47+
<thead>
48+
<tr>
49+
<th scope="col">#</th>
50+
<th scope="col">Provider</th>
51+
<th scope="col"></th>
52+
</tr>
53+
</thead>
54+
<tbody>
55+
<tr *ngFor="let provider of accountDetails.externalLogins; let i = index">
56+
<th scope="row">{{i+1}}</th>
57+
<td>{{provider}}</td>
58+
<td>
59+
<img src="/images/{{provider}}.png" style="width: 20px; margin: 5px 5px 10px;" />
60+
</td>
61+
</tr>
62+
</tbody>
63+
</table>
64+
</div>
65+
</div>
66+
<div class="row tab-pane fade" id="2fa">
67+
<div class="col-sm-6 col-sm-offset-3" style="margin-bottom:20px" *ngIf="!displayAuthenticator">
68+
<button type="button" (click)="setupAuthenticator()" class="form-control btn">Setup authenticator</button>
69+
</div>
70+
<div class="col-sm-6 col-sm-offset-3" style="margin-bottom:20px" *ngIf="displayAuthenticator">
71+
<p>Scan the QR Code or enter the following key into your two factor authenticator app. Spaces and casing do not matter</p>
72+
<div style="text-align: center; color: lightgreen;font-size: 20px;">{{authenticatorDetails.sharedKey}}</div>
73+
<div id="genQrCode"><span *ngIf="generatingQrCode">Generating...</span></div>
74+
<div>
75+
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
76+
<input class="form-control" type="text" placeholder="Verification Code" style="margin: 10px 0;" />
77+
<button type="button" (click)="setupAuthenticator()" class="form-control btn btn-primary">Verify</button>
78+
</div>
79+
</div>
80+
<div class="col-sm-6 col-sm-offset-3" *ngIf="accountDetails.hasAuthenticator">
81+
<button type="button" class="form-control btn btn-danger">Reset authenticator</button>
82+
83+
</div>
84+
</div>
85+
<div class="tab-pane fade" id="resetpassword">Default 5</div>
86+
</div>
87+
</div>
88+
</div>
89+
</div>
90+
</div>
Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,69 @@
11
import { Component, Inject } from '@angular/core';
22
import { Http } from '@angular/http';
33

4+
declare var QRCode: any;
5+
46
@Component({
57
selector: 'account',
68
templateUrl: './account.component.html',
79
styleUrls: ['./account.component.css']
810
})
911
export class AccountComponent {
1012

13+
public accountDetails: AccountDetailsVM = <AccountDetailsVM>{};
14+
public authenticatorDetails: AuthenticatorDetailsVM = <AuthenticatorDetailsVM>{};
15+
public displayAuthenticator: boolean = false;
16+
public generatingQrCode: boolean = false;
17+
18+
public generatedQRCode: any;
19+
1120
constructor(public http: Http, @Inject('BASE_URL') public baseUrl: string) {
12-
21+
this.http.get(this.baseUrl + 'api/manageaccount/details').subscribe(result => {
22+
this.accountDetails = result.json() as AccountDetailsVM;
23+
console.log(this.accountDetails);
24+
}, error => console.error(error));
25+
}
26+
27+
setupAuthenticator() {
28+
let self = this;
29+
this.http.get(this.baseUrl + 'api/manageaccount/setupAuthenticator').subscribe(result => {
30+
this.authenticatorDetails = result.json() as AuthenticatorDetailsVM;
31+
console.log(this.authenticatorDetails);
32+
this.displayAuthenticator = true;
33+
this.generatingQrCode = true;
34+
35+
setTimeout(function () {
36+
self.generatedQRCode = new QRCode(document.getElementById("genQrCode"),
37+
{
38+
text: self.authenticatorDetails.authenticatorUri,
39+
width: 150,
40+
height: 150,
41+
colorDark: "#000",
42+
colorLight: "#ffffff",
43+
correctLevel: QRCode.CorrectLevel.H
44+
});
45+
self.generatingQrCode = false;
46+
(document.querySelector("#genQrCode > img") as HTMLInputElement).style.margin = "0 auto";
47+
},
48+
1000);
49+
50+
}, error => console.error(error));
1351
}
52+
}
53+
54+
interface AccountDetailsVM {
55+
username: string;
56+
email: string;
57+
emailConfirmed: boolean;
58+
phoneNumber: string;
59+
externalLogins: string[];
60+
twoFactorEnabled: boolean;
61+
hasAuthenticator: boolean;
62+
twoFactorClientRemembered: boolean;
63+
recoveryCodesLeft: number[];
64+
}
65+
66+
interface AuthenticatorDetailsVM {
67+
sharedKey: string;
68+
authenticatorUri: string;
1469
}

AspNetCoreIdentity/ClientApp/app/components/navmenu/navmenu.component.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ li .glyphicon {
66
li.link-active a,
77
li.link-active a:hover,
88
li.link-active a:focus {
9-
background-color: #4189C7;
9+
background-color: #2b4162;
10+
background-image: linear-gradient(315deg, #2b4162 0%, #12100e 74%);
1011
color: white;
1112
}
1213

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Text.Encodings.Web;
6+
using System.Threading.Tasks;
7+
using AspNetCoreIdentity.ViewModels;
8+
using Microsoft.AspNetCore.Authorization;
9+
using Microsoft.AspNetCore.Identity;
10+
using Microsoft.AspNetCore.Mvc;
11+
12+
namespace AspNetCoreIdentity.Controllers
13+
{
14+
[Route("api/[controller]/[action]")]
15+
public class ManageAccountController : Controller
16+
{
17+
private readonly UserManager<IdentityUser> _userManager;
18+
private readonly SignInManager<IdentityUser> _signInManager;
19+
private readonly UrlEncoder _urlEncoder;
20+
private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
21+
22+
public ManageAccountController(UserManager<IdentityUser> userManager,
23+
SignInManager<IdentityUser> signInManager, UrlEncoder urlEncoder)
24+
{
25+
_userManager = userManager;
26+
_signInManager = signInManager;
27+
_urlEncoder = urlEncoder;
28+
}
29+
30+
[HttpGet]
31+
[Authorize]
32+
public async Task<AccountDetailsVM> Details()
33+
{
34+
var user = await _userManager.GetUserAsync(User);
35+
var logins = await _userManager.GetLoginsAsync(user);
36+
37+
return new AccountDetailsVM
38+
{
39+
Username = user.UserName,
40+
Email = user.Email,
41+
EmailConfirmed = user.EmailConfirmed,
42+
PhoneNumber = user.PhoneNumber,
43+
ExternalLogins = logins.Select(login => login.ProviderDisplayName).ToList(),
44+
TwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user),
45+
HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null,
46+
TwoFactorClientRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user),
47+
RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user)
48+
};
49+
}
50+
51+
[HttpGet]
52+
[Authorize]
53+
public async Task<AuthenticatorDetailsVM> SetupAuthenticator()
54+
{
55+
var user = await _userManager.GetUserAsync(User);
56+
var authenticatorDetails = await LoadSharedKeyAndQrCodeUriAsync(user);
57+
58+
return authenticatorDetails;
59+
}
60+
61+
private async Task<AuthenticatorDetailsVM> LoadSharedKeyAndQrCodeUriAsync(IdentityUser user)
62+
{
63+
// Load the authenticator key & QR code URI to display on the form
64+
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
65+
if (string.IsNullOrEmpty(unformattedKey))
66+
{
67+
await _userManager.ResetAuthenticatorKeyAsync(user);
68+
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
69+
}
70+
71+
var email = await _userManager.GetEmailAsync(user);
72+
73+
return new AuthenticatorDetailsVM
74+
{
75+
SharedKey = FormatKey(unformattedKey),
76+
AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey)
77+
};
78+
}
79+
80+
private string FormatKey(string unformattedKey)
81+
{
82+
var result = new StringBuilder();
83+
int currentPosition = 0;
84+
while (currentPosition + 4 < unformattedKey.Length)
85+
{
86+
result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
87+
currentPosition += 4;
88+
}
89+
if (currentPosition < unformattedKey.Length)
90+
{
91+
result.Append(unformattedKey.Substring(currentPosition));
92+
}
93+
94+
return result.ToString().ToLowerInvariant();
95+
}
96+
97+
private string GenerateQrCodeUri(string email, string unformattedKey)
98+
{
99+
return string.Format(
100+
AuthenticatorUriFormat,
101+
_urlEncoder.Encode("ExternalAuthApp"),
102+
_urlEncoder.Encode(email),
103+
unformattedKey);
104+
}
105+
}
106+
}

AspNetCoreIdentity/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
111111
routes.MapRoute(
112112
name: "default",
113113
template: "{controller=Home}/{action=Index}/{id?}");
114-
114+
115115
routes.MapSpaFallbackRoute(
116116
name: "spa-fallback",
117117
defaults: new { controller = "Home", action = "Index" });

0 commit comments

Comments
 (0)