Skip to content

Commit ffe4d3e

Browse files
committed
2fa qr code example
1 parent 51e5f80 commit ffe4d3e

14 files changed

+612
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ obj
55
.DS_Store
66
*.userprefs
77
.vs
8+
packages/

2FAQrCode/2FAQrCode.sln

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29102.190
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "2FAQrCode", "2FAQrCode\2FAQrCode.csproj", "{9D81373F-55F5-491A-8056-5DDE9543105C}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{9D81373F-55F5-491A-8056-5DDE9543105C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{9D81373F-55F5-491A-8056-5DDE9543105C}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{9D81373F-55F5-491A-8056-5DDE9543105C}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{9D81373F-55F5-491A-8056-5DDE9543105C}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {70A564BC-0910-4DD1-99FB-09FFFCF2FEB0}
24+
EndGlobalSection
25+
EndGlobal

2FAQrCode/2FAQrCode/2FAQrCode.csproj

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
4+
<PropertyGroup>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<ProjectGuid>{9D81373F-55F5-491A-8056-5DDE9543105C}</ProjectGuid>
8+
<OutputType>WinExe</OutputType>
9+
<RootNamespace>_2FAQrCode</RootNamespace>
10+
<AssemblyName>2FAQrCode</AssemblyName>
11+
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
12+
<FileAlignment>512</FileAlignment>
13+
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
14+
<WarningLevel>4</WarningLevel>
15+
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
16+
<Deterministic>true</Deterministic>
17+
</PropertyGroup>
18+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19+
<PlatformTarget>AnyCPU</PlatformTarget>
20+
<DebugSymbols>true</DebugSymbols>
21+
<DebugType>full</DebugType>
22+
<Optimize>false</Optimize>
23+
<OutputPath>bin\Debug\</OutputPath>
24+
<DefineConstants>DEBUG;TRACE</DefineConstants>
25+
<ErrorReport>prompt</ErrorReport>
26+
<WarningLevel>4</WarningLevel>
27+
</PropertyGroup>
28+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
29+
<PlatformTarget>AnyCPU</PlatformTarget>
30+
<DebugType>pdbonly</DebugType>
31+
<Optimize>true</Optimize>
32+
<OutputPath>bin\Release\</OutputPath>
33+
<DefineConstants>TRACE</DefineConstants>
34+
<ErrorReport>prompt</ErrorReport>
35+
<WarningLevel>4</WarningLevel>
36+
</PropertyGroup>
37+
<ItemGroup>
38+
<Reference Include="Otp.NET, Version=1.2.1.0, Culture=neutral, PublicKeyToken=38a48df817e173a6, processorArchitecture=MSIL">
39+
<HintPath>..\packages\Otp.NET.1.2.1\lib\net45\Otp.NET.dll</HintPath>
40+
</Reference>
41+
<Reference Include="QRCoder, Version=1.3.6.0, Culture=neutral, processorArchitecture=MSIL">
42+
<HintPath>..\packages\QRCoder.1.3.6\lib\net40\QRCoder.dll</HintPath>
43+
</Reference>
44+
<Reference Include="System" />
45+
<Reference Include="System.Data" />
46+
<Reference Include="System.Drawing" />
47+
<Reference Include="System.Drawing.Common, Version=4.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
48+
<HintPath>..\packages\System.Drawing.Common.4.5.1\lib\net461\System.Drawing.Common.dll</HintPath>
49+
</Reference>
50+
<Reference Include="System.Xml" />
51+
<Reference Include="Microsoft.CSharp" />
52+
<Reference Include="System.Core" />
53+
<Reference Include="System.Xml.Linq" />
54+
<Reference Include="System.Data.DataSetExtensions" />
55+
<Reference Include="System.Net.Http" />
56+
<Reference Include="System.Xaml">
57+
<RequiredTargetFramework>4.0</RequiredTargetFramework>
58+
</Reference>
59+
<Reference Include="WindowsBase" />
60+
<Reference Include="PresentationCore" />
61+
<Reference Include="PresentationFramework" />
62+
</ItemGroup>
63+
<ItemGroup>
64+
<ApplicationDefinition Include="App.xaml">
65+
<Generator>MSBuild:Compile</Generator>
66+
<SubType>Designer</SubType>
67+
</ApplicationDefinition>
68+
<Page Include="MainWindow.xaml">
69+
<Generator>MSBuild:Compile</Generator>
70+
<SubType>Designer</SubType>
71+
</Page>
72+
<Compile Include="App.xaml.cs">
73+
<DependentUpon>App.xaml</DependentUpon>
74+
<SubType>Code</SubType>
75+
</Compile>
76+
<Compile Include="MainWindow.xaml.cs">
77+
<DependentUpon>MainWindow.xaml</DependentUpon>
78+
<SubType>Code</SubType>
79+
</Compile>
80+
</ItemGroup>
81+
<ItemGroup>
82+
<Compile Include="Properties\AssemblyInfo.cs">
83+
<SubType>Code</SubType>
84+
</Compile>
85+
<Compile Include="Properties\Resources.Designer.cs">
86+
<AutoGen>True</AutoGen>
87+
<DesignTime>True</DesignTime>
88+
<DependentUpon>Resources.resx</DependentUpon>
89+
</Compile>
90+
<Compile Include="Properties\Settings.Designer.cs">
91+
<AutoGen>True</AutoGen>
92+
<DependentUpon>Settings.settings</DependentUpon>
93+
<DesignTimeSharedInput>True</DesignTimeSharedInput>
94+
</Compile>
95+
<EmbeddedResource Include="Properties\Resources.resx">
96+
<Generator>ResXFileCodeGenerator</Generator>
97+
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
98+
</EmbeddedResource>
99+
<None Include="packages.config" />
100+
<None Include="Properties\Settings.settings">
101+
<Generator>SettingsSingleFileGenerator</Generator>
102+
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
103+
</None>
104+
</ItemGroup>
105+
<ItemGroup>
106+
<None Include="App.config" />
107+
</ItemGroup>
108+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
109+
</Project>

2FAQrCode/2FAQrCode/App.config

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<configuration>
3+
<startup>
4+
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
5+
</startup>
6+
</configuration>

2FAQrCode/2FAQrCode/App.xaml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Application x:Class="_2FAQrCode.App"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:local="clr-namespace:_2FAQrCode"
5+
StartupUri="MainWindow.xaml">
6+
<Application.Resources>
7+
8+
</Application.Resources>
9+
</Application>

2FAQrCode/2FAQrCode/App.xaml.cs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Configuration;
4+
using System.Data;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using System.Windows;
8+
9+
namespace _2FAQrCode
10+
{
11+
/// <summary>
12+
/// Interaction logic for App.xaml
13+
/// </summary>
14+
public partial class App : Application
15+
{
16+
}
17+
}

2FAQrCode/2FAQrCode/MainWindow.xaml

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<Window x:Class="_2FAQrCode.MainWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
mc:Ignorable="d"
7+
Title="TOTP - QR Code" Height="560" Width="800" FontSize="20">
8+
<Window.Resources>
9+
<Style TargetType="{x:Type TextBox}">
10+
<Setter Property="Background" Value="LightYellow" />
11+
<Setter Property="VerticalContentAlignment" Value="Center" />
12+
<Setter Property="Width" Value="500" />
13+
</Style>
14+
<Style TargetType="{x:Type Label}">
15+
<Setter Property="HorizontalAlignment" Value="Left" />
16+
<Setter Property="Width" Value="130" />
17+
</Style>
18+
</Window.Resources>
19+
<Grid>
20+
<StackPanel>
21+
<StackPanel Orientation="Horizontal">
22+
<Label Content="Label:" />
23+
<TextBox Name="Label" TextChanged="Input_TextChanged" />
24+
</StackPanel>
25+
<StackPanel Orientation="Horizontal">
26+
<Label Content="Issuer:" />
27+
<TextBox Name="Issuer" TextChanged="Input_TextChanged" />
28+
</StackPanel>
29+
<StackPanel Orientation="Horizontal" Margin="0 0 0 20">
30+
<Label Content="Secret:" />
31+
<TextBox Name="Secret" TextChanged="Input_TextChanged" />
32+
</StackPanel>
33+
<StackPanel Orientation="Horizontal" Margin="0 0 0 20">
34+
<Label Content="QR Code:" />
35+
<Image Name="QrCode" Width="200" Height="200" />
36+
</StackPanel>
37+
<StackPanel Orientation="Horizontal" Margin="0 0 0 20">
38+
<Label Content="6-digit code:" />
39+
<TextBox Name="Code" />
40+
<Button Name="VerifyCode" Content="Verify Code" Margin="20 0" Padding="5"
41+
Click="VerifyCode_Click" />
42+
</StackPanel>
43+
<UniformGrid Columns="3">
44+
<UniformGrid.Resources>
45+
<Style TargetType="{x:Type Label}">
46+
<Setter Property="Width" Value="225" />
47+
</Style>
48+
</UniformGrid.Resources>
49+
<Label Content="Previous Code:" />
50+
<Label Content="Current Code:" />
51+
<Label Content="Next Code:" />
52+
</UniformGrid>
53+
<UniformGrid Columns="3">
54+
<UniformGrid.Resources>
55+
<Style TargetType="{x:Type Label}">
56+
<Setter Property="BorderBrush" Value="White" />
57+
<Setter Property="BorderThickness" Value="1" />
58+
<Setter Property="HorizontalAlignment" Value="Center" />
59+
</Style>
60+
</UniformGrid.Resources>
61+
<Label Content="Totp Code 1" Name="TotpCode1" />
62+
<Label Content="Totp Code 2" Name="TotpCode2" />
63+
<Label Content="Totp Code 3" Name="TotpCode3" />
64+
</UniformGrid>
65+
</StackPanel>
66+
</Grid>
67+
</Window>
+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using OtpNet;
2+
using QRCoder;
3+
using System;
4+
using System.Drawing.Imaging;
5+
using System.IO;
6+
using System.Windows;
7+
using System.Windows.Controls;
8+
using System.Windows.Media;
9+
using System.Windows.Media.Imaging;
10+
11+
namespace _2FAQrCode
12+
{
13+
/// <summary>
14+
/// Interaction logic for MainWindow.xaml
15+
/// </summary>
16+
public partial class MainWindow : Window
17+
{
18+
readonly QRCodeGenerator qrGenerator = new QRCodeGenerator();
19+
const int DEFAULT_STEP = 30;
20+
21+
public MainWindow()
22+
{
23+
InitializeComponent();
24+
25+
Label.Focus();
26+
Label.Text = "one";
27+
Issuer.Text = "two";
28+
Secret.Text = Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(20));
29+
}
30+
31+
private void Input_TextChanged(object sender, TextChangedEventArgs e)
32+
{
33+
if (Label.Text != "" && Issuer.Text != "" && Secret.Text != "")
34+
{
35+
var qrCodeUri = $"otpauth://totp/{Uri.EscapeDataString(Label.Text)}?secret={Secret.Text}&issuer={Uri.EscapeDataString(Issuer.Text)}";
36+
37+
using (var qrCodeData = qrGenerator.CreateQrCode(qrCodeUri, QRCodeGenerator.ECCLevel.Q))
38+
{
39+
using (var qrCode = new QRCode(qrCodeData))
40+
{
41+
var qrCodeImage = qrCode.GetGraphic(20);
42+
43+
using (var memory = new MemoryStream())
44+
{
45+
qrCodeImage.Save(memory, ImageFormat.Bmp);
46+
memory.Position = 0;
47+
var bitmapImage = new BitmapImage();
48+
bitmapImage.BeginInit();
49+
bitmapImage.StreamSource = memory;
50+
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
51+
bitmapImage.EndInit();
52+
QrCode.Source = bitmapImage;
53+
}
54+
}
55+
}
56+
QrCode.ToolTip = qrCodeUri;
57+
}
58+
}
59+
60+
private void VerifyCode_Click(object sender, RoutedEventArgs e)
61+
{
62+
var totp = new Totp(Base32Encoding.ToBytes(Secret.Text));
63+
64+
var isValidTotpCode = totp.VerifyTotp(Code.Text, out long timeStepMatched, new VerificationWindow(previous: 1, future: 1));
65+
66+
var now = DateTime.UtcNow;
67+
var step = Math.Floor((now - new DateTime(1970, 1, 1)).TotalSeconds / DEFAULT_STEP);
68+
69+
TotpCode1.Content = $"{totp.ComputeTotp(now.AddSeconds(-DEFAULT_STEP))}{Environment.NewLine}({step - 1})";
70+
TotpCode2.Content = $"{totp.ComputeTotp(now)}{Environment.NewLine}({step})";
71+
TotpCode3.Content = $"{totp.ComputeTotp(now.AddSeconds(DEFAULT_STEP))}{Environment.NewLine}({step + 1})";
72+
73+
foreach (var label in new Label[] { TotpCode1, TotpCode2, TotpCode3 })
74+
label.BorderBrush = Brushes.White;
75+
76+
if (isValidTotpCode)
77+
{
78+
if (Code.Text == TotpCode1.Content.ToString())
79+
TotpCode1.BorderBrush = Brushes.Green;
80+
else if (Code.Text == TotpCode2.Content.ToString())
81+
TotpCode2.BorderBrush = Brushes.Green;
82+
else if (Code.Text == TotpCode3.Content.ToString())
83+
TotpCode3.BorderBrush = Brushes.Green;
84+
85+
MessageBox.Show($"Valid TOTP Code (step: {timeStepMatched})!");
86+
}
87+
else
88+
MessageBox.Show("Invalid TOTP Code");
89+
}
90+
}
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Reflection;
2+
using System.Resources;
3+
using System.Runtime.CompilerServices;
4+
using System.Runtime.InteropServices;
5+
using System.Windows;
6+
7+
// General Information about an assembly is controlled through the following
8+
// set of attributes. Change these attribute values to modify the information
9+
// associated with an assembly.
10+
[assembly: AssemblyTitle("2FAQrCode")]
11+
[assembly: AssemblyDescription("")]
12+
[assembly: AssemblyConfiguration("")]
13+
[assembly: AssemblyCompany("")]
14+
[assembly: AssemblyProduct("2FAQrCode")]
15+
[assembly: AssemblyCopyright("Copyright © 2019")]
16+
[assembly: AssemblyTrademark("")]
17+
[assembly: AssemblyCulture("")]
18+
19+
// Setting ComVisible to false makes the types in this assembly not visible
20+
// to COM components. If you need to access a type in this assembly from
21+
// COM, set the ComVisible attribute to true on that type.
22+
[assembly: ComVisible(false)]
23+
24+
//In order to begin building localizable applications, set
25+
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
26+
//inside a <PropertyGroup>. For example, if you are using US english
27+
//in your source files, set the <UICulture> to en-US. Then uncomment
28+
//the NeutralResourceLanguage attribute below. Update the "en-US" in
29+
//the line below to match the UICulture setting in the project file.
30+
31+
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32+
33+
34+
[assembly: ThemeInfo(
35+
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36+
//(used if a resource is not found in the page,
37+
// or application resource dictionaries)
38+
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39+
//(used if a resource is not found in the page,
40+
// app, or any theme specific resource dictionaries)
41+
)]
42+
43+
44+
// Version information for an assembly consists of the following four values:
45+
//
46+
// Major Version
47+
// Minor Version
48+
// Build Number
49+
// Revision
50+
//
51+
// You can specify all the values or you can default the Build and Revision Numbers
52+
// by using the '*' as shown below:
53+
// [assembly: AssemblyVersion("1.0.*")]
54+
[assembly: AssemblyVersion("1.0.0.0")]
55+
[assembly: AssemblyFileVersion("1.0.0.0")]

0 commit comments

Comments
 (0)