diff --git a/src/App/App.csproj b/src/App/App.csproj
index 371c5fcc2..f5bb98ce8 100644
--- a/src/App/App.csproj
+++ b/src/App/App.csproj
@@ -64,6 +64,9 @@
CollectionsPage.xaml
+
+ ScanPage.xaml
+
SharePage.xaml
diff --git a/src/App/Pages/Vault/ScanPage.xaml b/src/App/Pages/Vault/ScanPage.xaml
new file mode 100644
index 000000000..e3f6c7ef0
--- /dev/null
+++ b/src/App/Pages/Vault/ScanPage.xaml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/App/Pages/Vault/ScanPage.xaml.cs b/src/App/Pages/Vault/ScanPage.xaml.cs
new file mode 100644
index 000000000..4937dd88d
--- /dev/null
+++ b/src/App/Pages/Vault/ScanPage.xaml.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+
+namespace Bit.App.Pages
+{
+ public partial class ScanPage : BaseContentPage
+ {
+ private readonly Action _callback;
+
+ private DateTime? _timerStarted = null;
+ private TimeSpan _timerMaxLength = TimeSpan.FromMinutes(3);
+
+ public ScanPage(Action callback)
+ {
+ _callback = callback;
+ _zxing.Options = new ZXing.Mobile.MobileBarcodeScanningOptions
+ {
+ UseNativeScanning = true,
+ PossibleFormats = new List { ZXing.BarcodeFormat.QR_CODE },
+ AutoRotate = false,
+ };
+ InitializeComponent();
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ _zxing.IsScanning = true;
+ _timerStarted = DateTime.Now;
+ Device.StartTimer(new TimeSpan(0, 0, 2), () =>
+ {
+ if(_timerStarted == null || (DateTime.Now - _timerStarted) > _timerMaxLength)
+ {
+ return false;
+ }
+ _zxing.AutoFocus();
+ return true;
+ });
+ }
+
+ protected override void OnDisappearing()
+ {
+ _timerStarted = null;
+ _zxing.IsScanning = false;
+ base.OnDisappearing();
+ }
+
+ private void OnScanResult(ZXing.Result result)
+ {
+ // Stop analysis until we navigate away so we don't keep reading barcodes
+ _zxing.IsAnalyzing = false;
+ _zxing.IsScanning = false;
+ if(!string.IsNullOrWhiteSpace(result?.Text) &&
+ Uri.TryCreate(result.Text, UriKind.Absolute, out Uri uri) &&
+ !string.IsNullOrWhiteSpace(uri?.Query))
+ {
+ var queryParts = uri.Query.Substring(1).ToLowerInvariant().Split('&');
+ foreach(var part in queryParts)
+ {
+ if(part.StartsWith("secret="))
+ {
+ _callback(part.Substring(7)?.ToUpperInvariant());
+ return;
+ }
+ }
+ }
+ _callback(null);
+ }
+ }
+}