Skip to content

Commit

Permalink
generate qrcode
Browse files Browse the repository at this point in the history
  • Loading branch information
chsakell committed Aug 4, 2019
1 parent 1308134 commit 080c5cf
Show file tree
Hide file tree
Showing 10 changed files with 380 additions and 6 deletions.
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;
}
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>
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ li .glyphicon {
li.link-active a,
li.link-active a:hover,
li.link-active a:focus {
background-color: #4189C7;
background-color: #2b4162;
background-image: linear-gradient(315deg, #2b4162 0%, #12100e 74%);
color: white;
}

Expand Down
106 changes: 106 additions & 0 deletions AspNetCoreIdentity/Controllers/ManageAccountController.cs
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);
}
}
}
2 changes: 1 addition & 1 deletion AspNetCoreIdentity/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");

routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
Expand Down
Loading

0 comments on commit 080c5cf

Please sign in to comment.