diff --git a/LibsAndSamples.sln b/LibsAndSamples.sln
index 8b6d414107..3af5aaf11c 100644
--- a/LibsAndSamples.sln
+++ b/LibsAndSamples.sln
@@ -36,8 +36,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreTestApp", "tests\dev
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DesktopTestApp", "tests\devapps\DesktopTestApp\DesktopTestApp.csproj", "{3A8EC886-C71D-4384-BFE4-73378CC7293D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp", "samples\desktop\SampleApp\SampleApp.csproj", "{85397CDA-120A-4626-9865-AD79EBFAC794}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Identity.Test.UIAutomation.Infrastructure", "tests\Microsoft.Identity.Test.Core.UIAutomation\Microsoft.Identity.Test.UIAutomation.Infrastructure.csproj", "{3DC6EC76-D350-4D43-B206-A4CEC8AA36D4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Identity.Test.Android.UIAutomation", "tests\Microsoft.Identity.Test.Android.UIAutomation\Microsoft.Identity.Test.Android.UIAutomation.csproj", "{5FF7878D-FEF2-4CAE-8ABA-36C01806D9CE}"
@@ -92,6 +90,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonCache.Test.Unit", "te
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Test.Integration", "tests\Microsoft.Identity.Test.Integration\Microsoft.Identity.Test.Integration.csproj", "{C98AAC90-C9D4-4DAD-975F-A898A7F89B2D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFxConsoleTestApp", "tests\devapps\NetFxConsoleTestApp\NetFxConsoleTestApp.csproj", "{24D2AF71-A01E-4450-8C3F-B9923A81134B}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonCache.Test.AdalV5", "tests\CacheCompat\CommonCache.Test.AdalV5\CommonCache.Test.AdalV5.csproj", "{4DDB6B3A-F95A-4F34-B0AE-E3C909D68A11}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonCache.Test.MsalJava", "tests\CacheCompat\CommonCache.Test.MsalJava\CommonCache.Test.MsalJava.csproj", "{B6BE589B-1DBB-4C36-803F-6D425723FA74}"
@@ -137,10 +137,6 @@ Global
{3A8EC886-C71D-4384-BFE4-73378CC7293D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A8EC886-C71D-4384-BFE4-73378CC7293D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A8EC886-C71D-4384-BFE4-73378CC7293D}.Release|Any CPU.Build.0 = Release|Any CPU
- {85397CDA-120A-4626-9865-AD79EBFAC794}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {85397CDA-120A-4626-9865-AD79EBFAC794}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {85397CDA-120A-4626-9865-AD79EBFAC794}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {85397CDA-120A-4626-9865-AD79EBFAC794}.Release|Any CPU.Build.0 = Release|Any CPU
{3DC6EC76-D350-4D43-B206-A4CEC8AA36D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DC6EC76-D350-4D43-B206-A4CEC8AA36D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DC6EC76-D350-4D43-B206-A4CEC8AA36D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -209,6 +205,10 @@ Global
{C98AAC90-C9D4-4DAD-975F-A898A7F89B2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C98AAC90-C9D4-4DAD-975F-A898A7F89B2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C98AAC90-C9D4-4DAD-975F-A898A7F89B2D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {24D2AF71-A01E-4450-8C3F-B9923A81134B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {24D2AF71-A01E-4450-8C3F-B9923A81134B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {24D2AF71-A01E-4450-8C3F-B9923A81134B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {24D2AF71-A01E-4450-8C3F-B9923A81134B}.Release|Any CPU.Build.0 = Release|Any CPU
{4DDB6B3A-F95A-4F34-B0AE-E3C909D68A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DDB6B3A-F95A-4F34-B0AE-E3C909D68A11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DDB6B3A-F95A-4F34-B0AE-E3C909D68A11}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -240,7 +240,6 @@ Global
{0B22271B-163F-429B-A472-4C7BF531CEE9} = {3761DEF3-458C-4CE2-BE94-4FC9D12217CC}
{72F0ADCA-3A0B-44AA-8129-21A1B27CD841} = {34BE693E-3496-45A4-B1D2-D3A0E068EEDB}
{3A8EC886-C71D-4384-BFE4-73378CC7293D} = {34BE693E-3496-45A4-B1D2-D3A0E068EEDB}
- {85397CDA-120A-4626-9865-AD79EBFAC794} = {34BE693E-3496-45A4-B1D2-D3A0E068EEDB}
{3DC6EC76-D350-4D43-B206-A4CEC8AA36D4} = {9B0B5396-4D95-4C15-82ED-DC22B5A3123F}
{5FF7878D-FEF2-4CAE-8ABA-36C01806D9CE} = {9B0B5396-4D95-4C15-82ED-DC22B5A3123F}
{A181778D-5917-41CE-AA5F-7DAAA7B7F5BB} = {9B0B5396-4D95-4C15-82ED-DC22B5A3123F}
@@ -262,6 +261,7 @@ Global
{B6BE589B-1DBB-4C36-803F-6D425723FA74} = {58B95AA7-BE17-40F2-9C9A-FA27C282A10A}
{CA3F8F2E-7DF2-45A6-BC81-5DA5692FCC11} = {58B95AA7-BE17-40F2-9C9A-FA27C282A10A}
{2EAA7878-3E2A-4355-9841-430BF45EA8BD} = {58B95AA7-BE17-40F2-9C9A-FA27C282A10A}
+ {24D2AF71-A01E-4450-8C3F-B9923A81134B} = {34BE693E-3496-45A4-B1D2-D3A0E068EEDB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {020399A9-DC27-4B82-9CAA-EF488665AC27}
diff --git a/samples/desktop/SampleApp/App.config b/samples/desktop/SampleApp/App.config
deleted file mode 100644
index 3dbff35f48..0000000000
--- a/samples/desktop/SampleApp/App.config
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/samples/desktop/SampleApp/CachePersistence.cs b/samples/desktop/SampleApp/CachePersistence.cs
deleted file mode 100644
index 94ec3bf8df..0000000000
--- a/samples/desktop/SampleApp/CachePersistence.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// Copyright (c) Microsoft Corporation.
-// All rights reserved.
-//
-// This code is licensed under the MIT License.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-//------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.Identity.Client;
-
-namespace SampleApp
-{
- class CachePersistence
- {
- private static readonly TokenCache userTokenCache = new TokenCache();
-
- public static TokenCache GetUserCache()
- {
- lock (FileLock)
- {
- userTokenCache.SetBeforeAccess(BeforeAccessNotification);
- userTokenCache.SetAfterAccess(AfterAccessNotification);
- return userTokenCache;
- }
- }
-
- public static string CacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + "msalcache.txt";
-
- private static readonly object FileLock = new object();
-
- public static void BeforeAccessNotification(TokenCacheNotificationArgs args)
- {
- lock (FileLock)
- {
- args.TokenCache.DeserializeMsalV3(File.Exists(CacheFilePath)
- ? File.ReadAllBytes(CacheFilePath)
- : null);
- }
- }
-
- public static void AfterAccessNotification(TokenCacheNotificationArgs args)
- {
- // if the access operation resulted in a cache update
- if (args.HasStateChanged)
- {
- lock (FileLock)
- {
- // reflect changesgs in the persistent store
- File.WriteAllBytes(CacheFilePath, args.TokenCache.SerializeMsalV3());
- }
- }
- }
- }
-}
diff --git a/samples/desktop/SampleApp/MainForm.Designer.cs b/samples/desktop/SampleApp/MainForm.Designer.cs
deleted file mode 100644
index 026114effc..0000000000
--- a/samples/desktop/SampleApp/MainForm.Designer.cs
+++ /dev/null
@@ -1,168 +0,0 @@
-namespace SampleApp
-{
- partial class MainForm
- {
- ///
- /// Required designer variable.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null))
- {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Windows Form Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent()
- {
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
- this.tabControl1 = new System.Windows.Forms.TabControl();
- this.signInPage = new System.Windows.Forms.TabPage();
- this.acquireTokenUsernamePasswordButton = new System.Windows.Forms.Button();
- this.signOutButton1 = new System.Windows.Forms.Button();
- this.tokenResultBox = new System.Windows.Forms.TextBox();
- this.pictureBox1 = new System.Windows.Forms.PictureBox();
- this.calendarPage = new System.Windows.Forms.TabPage();
- this.calendarTextBox = new System.Windows.Forms.TextBox();
- this.acquireTokenWIAButton = new System.Windows.Forms.Button();
- this.tabControl1.SuspendLayout();
- this.signInPage.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
- this.SuspendLayout();
- //
- // tabControl1
- //
- this.tabControl1.Controls.Add(this.signInPage);
- this.tabControl1.Controls.Add(this.calendarPage);
- this.tabControl1.Location = new System.Drawing.Point(-1, -1);
- this.tabControl1.Margin = new System.Windows.Forms.Padding(2);
- this.tabControl1.Name = "tabControl1";
- this.tabControl1.SelectedIndex = 0;
- this.tabControl1.Size = new System.Drawing.Size(542, 383);
- this.tabControl1.TabIndex = 0;
- //
- // signInPage
- //
- this.signInPage.Controls.Add(this.acquireTokenWIAButton);
- this.signInPage.Controls.Add(this.acquireTokenUsernamePasswordButton);
- this.signInPage.Controls.Add(this.signOutButton1);
- this.signInPage.Controls.Add(this.tokenResultBox);
- this.signInPage.Controls.Add(this.pictureBox1);
- this.signInPage.Location = new System.Drawing.Point(4, 22);
- this.signInPage.Margin = new System.Windows.Forms.Padding(2);
- this.signInPage.Name = "signInPage";
- this.signInPage.Padding = new System.Windows.Forms.Padding(2);
- this.signInPage.Size = new System.Drawing.Size(534, 357);
- this.signInPage.TabIndex = 0;
- this.signInPage.Text = "signInPage";
- this.signInPage.UseVisualStyleBackColor = true;
- //
- // acquireTokenUsernamePasswordButton
- //
- this.acquireTokenUsernamePasswordButton.Location = new System.Drawing.Point(21, 83);
- this.acquireTokenUsernamePasswordButton.Name = "acquireTokenUsernamePasswordButton";
- this.acquireTokenUsernamePasswordButton.Size = new System.Drawing.Size(227, 40);
- this.acquireTokenUsernamePasswordButton.TabIndex = 3;
- this.acquireTokenUsernamePasswordButton.Text = "Sign in with username/password";
- this.acquireTokenUsernamePasswordButton.UseVisualStyleBackColor = true;
- this.acquireTokenUsernamePasswordButton.Click += new System.EventHandler(this.acquireTokenUsernamePasswordButton_Click);
- //
- // signOutButton1
- //
- this.signOutButton1.Location = new System.Drawing.Point(426, 319);
- this.signOutButton1.Name = "signOutButton1";
- this.signOutButton1.Size = new System.Drawing.Size(79, 25);
- this.signOutButton1.TabIndex = 2;
- this.signOutButton1.Text = "Sign Out";
- this.signOutButton1.UseVisualStyleBackColor = true;
- this.signOutButton1.Click += new System.EventHandler(this.signOutButton1_Click);
- //
- // tokenResultBox
- //
- this.tokenResultBox.Location = new System.Drawing.Point(19, 213);
- this.tokenResultBox.Multiline = true;
- this.tokenResultBox.Name = "tokenResultBox";
- this.tokenResultBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
- this.tokenResultBox.Size = new System.Drawing.Size(366, 96);
- this.tokenResultBox.TabIndex = 1;
- //
- // pictureBox1
- //
- this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
- this.pictureBox1.Location = new System.Drawing.Point(19, 27);
- this.pictureBox1.Margin = new System.Windows.Forms.Padding(2);
- this.pictureBox1.Name = "pictureBox1";
- this.pictureBox1.Size = new System.Drawing.Size(230, 40);
- this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
- this.pictureBox1.TabIndex = 0;
- this.pictureBox1.TabStop = false;
- this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);
- //
- // calendarPage
- //
- this.calendarPage.Location = new System.Drawing.Point(4, 22);
- this.calendarPage.Name = "calendarPage";
- this.calendarPage.Size = new System.Drawing.Size(534, 357);
- this.calendarPage.TabIndex = 1;
- //
- // calendarTextBox
- //
- this.calendarTextBox.Location = new System.Drawing.Point(0, 0);
- this.calendarTextBox.Name = "calendarTextBox";
- this.calendarTextBox.Size = new System.Drawing.Size(100, 20);
- this.calendarTextBox.TabIndex = 0;
- //
- // acquireTokenWIAButton
- //
- this.acquireTokenWIAButton.Location = new System.Drawing.Point(21, 139);
- this.acquireTokenWIAButton.Name = "acquireTokenWIAButton";
- this.acquireTokenWIAButton.Size = new System.Drawing.Size(227, 41);
- this.acquireTokenWIAButton.TabIndex = 4;
- this.acquireTokenWIAButton.Text = "Acquire token with Integrated Windows Auth";
- this.acquireTokenWIAButton.UseVisualStyleBackColor = true;
- this.acquireTokenWIAButton.Click += new System.EventHandler(this.acquireTokenWIAButton_Click);
- //
- // MainForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(539, 378);
- this.Controls.Add(this.tabControl1);
- this.Margin = new System.Windows.Forms.Padding(2);
- this.Name = "MainForm";
- this.Text = "MainForm";
- this.tabControl1.ResumeLayout(false);
- this.signInPage.ResumeLayout(false);
- this.signInPage.PerformLayout();
- ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
- this.ResumeLayout(false);
-
- }
-
- #endregion
-
- private System.Windows.Forms.TabControl tabControl1;
- private System.Windows.Forms.TabPage signInPage;
- private System.Windows.Forms.TabPage calendarPage;
- private System.Windows.Forms.PictureBox pictureBox1;
- private System.Windows.Forms.TextBox tokenResultBox;
- private System.Windows.Forms.TextBox calendarTextBox;
- private System.Windows.Forms.Button signOutButton1;
- private System.Windows.Forms.Button acquireTokenUsernamePasswordButton;
- private System.Windows.Forms.Button acquireTokenWIAButton;
- }
-}
-
diff --git a/samples/desktop/SampleApp/MainForm.cs b/samples/desktop/SampleApp/MainForm.cs
deleted file mode 100644
index 14cf108e2f..0000000000
--- a/samples/desktop/SampleApp/MainForm.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// Copyright (c) Microsoft Corporation.
-// All rights reserved.
-//
-// This code is licensed under the MIT License.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-//------------------------------------------------------------------------------
-
-using System;
-using System.Drawing;
-using System.Linq;
-using System.Security;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using Microsoft.Identity.Client;
-
-namespace SampleApp
-{
- public partial class MainForm : Form
- {
- private readonly MsalAuthHelper _msalHelper = new MsalAuthHelper("1950a258-227b-4e31-a9cf-717495945fc2");
- private IAccount account = null;
- private string token;
-
- public MainForm()
- {
- InitializeComponent();
- tabControl1.Appearance = TabAppearance.FlatButtons;
- tabControl1.ItemSize = new Size(0, 1);
- tabControl1.SizeMode = TabSizeMode.Fixed;
- account = _msalHelper.Application.GetAccountsAsync().Result.FirstOrDefault();
- tabControl1.SelectedIndexChanged += TabControl1_SelectedIndexChanged;
-
- signInPage.BackColor = Color.FromArgb(255, 67, 143, 255);
- if (account != null)
- {
- tabControl1.SelectedTab = calendarPage;
- }
- }
-
- private async void TabControl1_SelectedIndexChanged(object sender, EventArgs e)
- {
- if ((sender as TabControl).TabIndex == 1)
- {
- token = await _msalHelper.GetTokenForCurrentAccountAsync(new[] { "user.read" }, account)
- .ConfigureAwait(true);
- }
- }
-
- private void pictureBox1_Click(object sender, EventArgs e)
- {
- AcquireTokenAsync().Wait();
- UpdateResponse(token);
- }
-
- private async Task AcquireTokenAsync()
- {
- token = await _msalHelper.GetTokenForCurrentAccountAsync(new[] { "user.read" }, account).ConfigureAwait(true);
- }
-
- private async void acquireTokenUsernamePasswordButton_Click(object sender, EventArgs e)
- {
- tokenResultBox.Text = string.Empty;
- SecureString securePassword = ConvertToSecureString(tokenResultBox);
- token = await _msalHelper.GetTokenWithUsernamePasswordAsync(new[] { "user.read" }, securePassword).ConfigureAwait(true);
- }
-
- private SecureString ConvertToSecureString(TextBox textBox)
- {
- if (tokenResultBox.Text.Length > 0)
- {
- SecureString securePassword = new SecureString();
- tokenResultBox.Text.ToCharArray().ToList().ForEach(p => securePassword.AppendChar(p));
- securePassword.MakeReadOnly();
- return securePassword;
- }
- return null;
- }
-
- private void UpdateResponse(string token)
- {
- if (token != null)
- {
- tokenResultBox.Text = "Result:\n" + token;
- }
- else
- {
- tokenResultBox.Text = "Authentication failed. No access token returned";
- }
- }
-
- private async void signOutButton1_Click(object sender, EventArgs e)
- {
- var accounts = await _msalHelper.Application.GetAccountsAsync().ConfigureAwait(false);
-
- if (accounts.Any())
- {
- try
- {
- await _msalHelper.Application.RemoveAsync(accounts.FirstOrDefault()).ConfigureAwait(true);
- tokenResultBox.Text = "User has signed-out";
- }
- catch (MsalException ex)
- {
- tokenResultBox.Text = $"Error signing-out user: {ex.Message}";
- }
- }
-
- }
-
- private async void acquireTokenWIAButton_Click(object sender, EventArgs e)
- {
- string user = "";
- token = await _msalHelper.GetTokenWithIWAAsync(new[] { "user.read" }, user).ConfigureAwait(true);
- }
- }
-}
diff --git a/samples/desktop/SampleApp/MainForm.resx b/samples/desktop/SampleApp/MainForm.resx
deleted file mode 100644
index 31c5d86faa..0000000000
--- a/samples/desktop/SampleApp/MainForm.resx
+++ /dev/null
@@ -1,151 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAAOYAAAAoCAIAAABl6hEoAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAL
- EwAACxMBAJqcGAAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAVdSURBVHhe7Zex
- ThwxEIZ5ljxR3oUWKR0VEi01SkdLkxS0tNS0tEiRUtAmH3zLaGLvHXcR3J6P+YRO9tgej2f+9S5HZ0Ux
- FCXZYjAmyf4pir1HrZZki2FQqyXZYhjUakm2GAa1WpIthkGtlmSLYVCrJdliGNRqSbYYBrVaki2GQa1u
- JNnfX7/M/jH07efR7J8Li+IdUasl2WU4OTk5Pj5+eHiY+p+Du7u78/NzDs7xJ9M2qNXFJEv0l5eXRC+n
- p6dYsD89PdHmSI+Pj858Rz7UeXB/f8+JqM3Un2Mryd7c3Lwk6bnSHGGyJtQBMJPubo65LQTjqYHGZN0G
- tbqMZG9vbw09c319zdBBShYl0c0CtXjbShYUZQYn09h+S9ZT5LQQJ0Un1Kn/Fmp1GclasIuLC4LWwnlM
- 90HSC/Q/JOuS/vKm6tgpPL/7nMOrqysi9GISHzbONfXfQq0uIFlkSqDAbTSZDp13kSxiVZfNKi9URhna
- Z8n6HTikZMHUc8tO/X9pyonE46uXJbzsbHtDe8cwgfmWjeV8eLi2Jzvn69n5uMIzbcg5zTghNBFhxMvX
- CWydK6HaMnqIMIwfWB7vnIaQrJNzhB4BSyPZfExhyDlAw7BjVXMFYrFGQCPcSsQMLJysLxLM/5/QdhcT
- kmEoz5Qc7SxqdeFvWTLbpAOadEeiJbpZstTbVcGq82fnIdlmiz4kwMgQG+UuxOOBH6BhhWzHtEDnjNJW
- 5UE4b9AJQeoZDU0Dr88J9hCfdv1HEnp9+M+uq+L4SraJSkKazajHBL/gGxhFtYad+XDJfgSkLD/HZlBy
- uq0WFifk6zBLFqwWCdJtXBgN2bmShdCKzimh3QyeGYoKMVNX1tKC6ccKxUzIm4oWQvUe8pLLSzIhWdrK
- y2wYUrbPSjaOGTlhmh5cBXYdosvyeBS1AN7IuW0/6uiiPKeZdjJgXYhN5zFBjea69Ilaj1rdSLJH33/N
- /j2P/Tia/9sM0hHCzdcVXdOthvI5rRNkyVo2sfyRqYa+llh0BV7/q5JoqFaLOWyNBehaV35p95XIm4qW
- OHIcSgU36NwzGqGny5uukWyvlaBRFfQJh+zcNtNiL/CJZdPIJER67R6IZMXDWHvI6TZBUVogKVggSzYn
- vbdksvMmp9BbMnqmVFaIXx8PdGalFVxfibypNJY4VJ4TsCNDpMIua4ElZMwGxqwq54S3PodBswp6C2S1
- xWGBXXQ7mzfzAEZ4UJJVAeDZ+nTnDDaJ2KVkjZOCsYVzvPP4fbltp0eur0TeVBoLZ6HbzAkaycZrxF+N
- TaKy/9mLU/r0zk7upxGwYQBJO/BbllORF84z9V/TFKHndKs/LM7Pj/juJQsMAerUP/EwvylzXwnaWHLJ
- tRgGbCXZeMKBtsY1knV5HqVhPnstOpnlcSvHcrxZO18m4MFdTk5oY7EuzNF5FGKVZGH21D1qdRnJGmhD
- JC6nm8l2A08OpmbHknV3JkRFKZU+Qz1WIjuxnOIxXRKlipzMFk/RhGRBNcS9DmskC45mVkkW4lLIOGe2
- djmZDURomaCXrA98MHv2jFpd5sOAuHMSyZEZlCbdNCKJvIzinItI1i8BiJtGPeUlOO8toVq1nsOAbSWr
- JUttvWTBtAjJMf5ZyQKT9QAkP55PiO8BYChvQfay3HESeoVeshD5hEjpKtTqXnzLboXVyoIoPglqdd8l
- y2OaH2UeSp/+VZdoccCo1Y0kuyDxxsyg2jdfIsXhoVb3XbKQv6uAbun1c6JWB5BsUYhaLckWw6BWS7LF
- MKjVkmwxDGq1JFsMg1otyRbDoFZLssUwqNWSbDEManWSbFGMQkm2GIqzs7/MHPG5WEiSKQAAAABJRU5E
- rkJggg==
-
-
-
\ No newline at end of file
diff --git a/samples/desktop/SampleApp/MsalAuthHelper.cs b/samples/desktop/SampleApp/MsalAuthHelper.cs
deleted file mode 100644
index fb6de3ef38..0000000000
--- a/samples/desktop/SampleApp/MsalAuthHelper.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// Copyright (c) Microsoft Corporation.
-// All rights reserved.
-//
-// This code is licensed under the MIT License.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-//------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Security;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using Microsoft.Identity.Client;
-
-namespace SampleApp
-{
- class MsalAuthHelper
- {
- private readonly string _clientId;
- private readonly string user = ""; //can be empty for IWA and U/P
-
- public PublicClientApplication Application { get; private set; }
-
- public MsalAuthHelper(string clientId)
- {
- _clientId = clientId;
- Application = new PublicClientApplication(_clientId, "https://login.microsoftonline.com/organizations/",
- CachePersistence.GetUserCache());
- }
-
- public async Task GetTokenForCurrentAccountAsync(IEnumerable scopes, IAccount account)
- {
- AuthenticationResult result = null;
- try
- {
- result = await Application.AcquireTokenSilentAsync(scopes, account).ConfigureAwait(false);
- return result.AccessToken;
- }
- catch (MsalUiRequiredException)
- {
- result = await Application.AcquireTokenAsync(scopes).ConfigureAwait(false);
- return result.AccessToken;
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message, "Failed to get token", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
-
- return null;
- }
-
- public async Task GetTokenWithUsernamePasswordAsync(IEnumerable scopes, SecureString password)
- {
- AuthenticationResult result = null;
-
- try
- {
- result = await Application.AcquireTokenByUsernamePasswordAsync(scopes, user, password).ConfigureAwait(false);
- return result.AccessToken;
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message, "Failed to get token", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
-
- return null;
- }
-
- internal async Task GetTokenWithIWAAsync(string[] scopes, string user)
- {
- AuthenticationResult result = null;
-
- try
- {
- result = await Application.AcquireTokenByIntegratedWindowsAuthAsync(scopes, user).ConfigureAwait(false);
- return result.AccessToken;
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message, "Failed to get token", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
-
- return null;
- }
- }
-}
diff --git a/samples/desktop/SampleApp/Properties/AssemblyInfo.cs b/samples/desktop/SampleApp/Properties/AssemblyInfo.cs
deleted file mode 100644
index ce47ab7460..0000000000
--- a/samples/desktop/SampleApp/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// Copyright (c) Microsoft Corporation.
-// All rights reserved.
-//
-// This code is licensed under the MIT License.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-//------------------------------------------------------------------------------
-
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("SampleApp")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SampleApp")]
-[assembly: AssemblyCopyright("Copyright © 2017")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("85397cda-120a-4626-9865-ad79ebfac794")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/samples/desktop/SampleApp/Properties/Resources.Designer.cs b/samples/desktop/SampleApp/Properties/Resources.Designer.cs
deleted file mode 100644
index cee8fb430d..0000000000
--- a/samples/desktop/SampleApp/Properties/Resources.Designer.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace SampleApp.Properties {
- using System;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SampleApp.Properties.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
- }
-}
diff --git a/samples/desktop/SampleApp/Properties/Resources.resx b/samples/desktop/SampleApp/Properties/Resources.resx
deleted file mode 100644
index af7dbebbac..0000000000
--- a/samples/desktop/SampleApp/Properties/Resources.resx
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/samples/desktop/SampleApp/Properties/Settings.Designer.cs b/samples/desktop/SampleApp/Properties/Settings.Designer.cs
deleted file mode 100644
index 9d3c33171c..0000000000
--- a/samples/desktop/SampleApp/Properties/Settings.Designer.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace SampleApp.Properties {
-
-
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.0.0.0")]
- internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
-
- private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-
- public static Settings Default {
- get {
- return defaultInstance;
- }
- }
- }
-}
diff --git a/samples/desktop/SampleApp/Properties/Settings.settings b/samples/desktop/SampleApp/Properties/Settings.settings
deleted file mode 100644
index 39645652af..0000000000
--- a/samples/desktop/SampleApp/Properties/Settings.settings
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/samples/desktop/SampleApp/no_photo.png b/samples/desktop/SampleApp/no_photo.png
deleted file mode 100644
index d1cc67cc29..0000000000
Binary files a/samples/desktop/SampleApp/no_photo.png and /dev/null differ
diff --git a/samples/desktop/SampleApp/sign-in-with-microsoft-light.png b/samples/desktop/SampleApp/sign-in-with-microsoft-light.png
deleted file mode 100644
index 7fd7bab862..0000000000
Binary files a/samples/desktop/SampleApp/sign-in-with-microsoft-light.png and /dev/null differ
diff --git a/src/Microsoft.Identity.Client/Cache/CacheFallbackOperations.cs b/src/Microsoft.Identity.Client/Cache/CacheFallbackOperations.cs
index 5358f16d90..c930abd3cf 100644
--- a/src/Microsoft.Identity.Client/Cache/CacheFallbackOperations.cs
+++ b/src/Microsoft.Identity.Client/Cache/CacheFallbackOperations.cs
@@ -57,7 +57,13 @@ public static void WriteAdalRefreshToken(
{
if (rtItem == null)
{
- logger.Info("No refresh token available. Skipping MSAL refresh token cache write");
+ logger.Info("No refresh token available. Skipping writing to ADAL legacy cache.");
+ return;
+ }
+
+ if (!string.IsNullOrEmpty(rtItem.FamilyId))
+ {
+ logger.Info("Not writing FRT in ADAL legacy cache");
return;
}
@@ -82,7 +88,10 @@ public static void WriteAdalRefreshToken(
ResourceInResponse = scope
};
- IDictionary dictionary = AdalCacheOperations.Deserialize(logger, legacyCachePersistence.LoadCache());
+ IDictionary dictionary = AdalCacheOperations.Deserialize(
+ logger,
+ legacyCachePersistence.LoadCache());
+
dictionary[key] = wrapper;
legacyCachePersistence.WriteCache(AdalCacheOperations.Serialize(logger, dictionary));
}
diff --git a/src/Microsoft.Identity.Client/Cache/CacheSessionManager.cs b/src/Microsoft.Identity.Client/Cache/CacheSessionManager.cs
index 48706212d8..d013416a59 100644
--- a/src/Microsoft.Identity.Client/Cache/CacheSessionManager.cs
+++ b/src/Microsoft.Identity.Client/Cache/CacheSessionManager.cs
@@ -52,9 +52,9 @@ public Task FindAccessTokenAsync()
return TokenCacheInternal.FindAccessTokenAsync(_requestParams);
}
- public Tuple SaveAccessAndRefreshToken(MsalTokenResponse tokenResponse)
+ public Tuple SaveTokenResponse(MsalTokenResponse tokenResponse)
{
- return TokenCacheInternal.SaveAccessAndRefreshToken(_requestParams, tokenResponse);
+ return TokenCacheInternal.SaveTokenResponse(_requestParams, tokenResponse);
}
public MsalIdTokenCacheItem GetIdTokenCacheItem(MsalIdTokenCacheKey idTokenCacheKey)
@@ -62,9 +62,24 @@ public MsalIdTokenCacheItem GetIdTokenCacheItem(MsalIdTokenCacheKey idTokenCache
return TokenCacheInternal.GetIdTokenCacheItem(idTokenCacheKey, _requestParams.RequestContext);
}
+ public Task FindFamilyRefreshTokenAsync(string familyId)
+ {
+ if (String.IsNullOrEmpty(familyId))
+ {
+ throw new ArgumentNullException(nameof(familyId));
+ }
+
+ return TokenCacheInternal.FindRefreshTokenAsync(_requestParams, familyId);
+ }
+
public Task FindRefreshTokenAsync()
{
return TokenCacheInternal.FindRefreshTokenAsync(_requestParams);
}
+
+ public Task IsAppFociMemberAsync(string familyId)
+ {
+ return TokenCacheInternal.IsFociMemberAsync(_requestParams, familyId);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/ICacheSessionManager.cs b/src/Microsoft.Identity.Client/Cache/ICacheSessionManager.cs
index 5056846761..816b75dfde 100644
--- a/src/Microsoft.Identity.Client/Cache/ICacheSessionManager.cs
+++ b/src/Microsoft.Identity.Client/Cache/ICacheSessionManager.cs
@@ -38,8 +38,10 @@ internal interface ICacheSessionManager
ITokenCacheInternal TokenCacheInternal { get; }
bool HasCache { get; }
Task FindAccessTokenAsync();
- Tuple SaveAccessAndRefreshToken(MsalTokenResponse tokenResponse);
+ Tuple SaveTokenResponse(MsalTokenResponse tokenResponse);
MsalIdTokenCacheItem GetIdTokenCacheItem(MsalIdTokenCacheKey idTokenCacheKey);
Task FindRefreshTokenAsync();
+ Task FindFamilyRefreshTokenAsync(string familyId);
+ Task IsAppFociMemberAsync(string familyId);
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs b/src/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs
index bc8695eab9..140ab0498a 100644
--- a/src/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs
+++ b/src/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs
@@ -33,13 +33,6 @@ namespace Microsoft.Identity.Client.Cache
{
internal interface ITokenCacheAccessor
{
- int RefreshTokenCount { get; }
- int AccessTokenCount { get; }
- int AccountCount { get; }
- int IdTokenCount { get; }
- void ClearRefreshTokens();
- void ClearAccessTokens();
-
void SaveAccessToken(MsalAccessTokenCacheItem item);
void SaveRefreshToken(MsalRefreshTokenCacheItem item);
@@ -48,6 +41,8 @@ internal interface ITokenCacheAccessor
void SaveAccount(MsalAccountCacheItem item);
+ void SaveAppMetadata(MsalAppMetadataCacheItem item);
+
MsalAccessTokenCacheItem GetAccessToken(MsalAccessTokenCacheKey accessTokenKey);
MsalRefreshTokenCacheItem GetRefreshToken(MsalRefreshTokenCacheKey refreshTokenKey);
@@ -56,6 +51,8 @@ internal interface ITokenCacheAccessor
MsalAccountCacheItem GetAccount(MsalAccountCacheKey accountKey);
+ MsalAppMetadataCacheItem GetAppMetadata(MsalAppMetadataCacheKey appMetadataKey);
+
void DeleteAccessToken(MsalAccessTokenCacheKey cacheKey);
void DeleteRefreshToken(MsalRefreshTokenCacheKey cacheKey);
@@ -64,13 +61,16 @@ internal interface ITokenCacheAccessor
void DeleteAccount(MsalAccountCacheKey cacheKey);
- ICollection GetAllAccessTokens();
+ IEnumerable GetAllAccessTokens();
+
+ IEnumerable GetAllRefreshTokens();
- ICollection GetAllRefreshTokens();
+ IEnumerable GetAllIdTokens();
- ICollection GetAllIdTokens();
+ IEnumerable GetAllAccounts();
+
+ IEnumerable GetAllAppMetadata();
- ICollection GetAllAccounts();
#if iOS
void SetiOSKeychainSecurityGroup(string keychainSecurityGroup);
diff --git a/src/Microsoft.Identity.Client/Cache/Items/CacheSerializationContract.cs b/src/Microsoft.Identity.Client/Cache/Items/CacheSerializationContract.cs
index cd2af0eaa8..4c9c4cc819 100644
--- a/src/Microsoft.Identity.Client/Cache/Items/CacheSerializationContract.cs
+++ b/src/Microsoft.Identity.Client/Cache/Items/CacheSerializationContract.cs
@@ -25,10 +25,7 @@
//
// ------------------------------------------------------------------------------
-using System;
using System.Collections.Generic;
-using System.Diagnostics;
-using Microsoft.Identity.Json;
using Microsoft.Identity.Json.Linq;
namespace Microsoft.Identity.Client.Cache.Items
@@ -41,9 +38,14 @@ internal class CacheSerializationContract
public Dictionary RefreshTokens { get; set; } =
new Dictionary();
- public Dictionary IdTokens { get; set; } = new Dictionary();
+ public Dictionary IdTokens { get; set; } =
+ new Dictionary();
- public Dictionary Accounts { get; set; } = new Dictionary();
+ public Dictionary Accounts { get; set; } =
+ new Dictionary();
+
+ public Dictionary AppMetadata { get; set; } =
+ new Dictionary();
internal static CacheSerializationContract FromJsonString(string json)
{
@@ -106,6 +108,20 @@ internal static CacheSerializationContract FromJsonString(string json)
}
}
+ // App Metadata
+ if (root.ContainsKey(StorageJsonValues.AppMetadata))
+ {
+ foreach (var token in root[StorageJsonValues.AppMetadata]
+ .Values())
+ {
+ if (token is JObject j)
+ {
+ var item = MsalAppMetadataCacheItem.FromJObject(j);
+ contract.AppMetadata[item.GetKey().ToString()] = item;
+ }
+ }
+ }
+
return contract;
}
@@ -149,7 +165,17 @@ internal string ToJsonString()
root[StorageJsonValues.AccountRootKey] = accountsRoot;
+ // App Metadata
+ var appMetadataRoot = new JObject();
+ foreach (var kvp in AppMetadata)
+ {
+ appMetadataRoot[kvp.Key] = kvp.Value.ToJObject();
+ }
+
+ root[StorageJsonValues.AppMetadata] = appMetadataRoot;
+
+
return root.ToString();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Items/MsalAccountCacheItem.cs b/src/Microsoft.Identity.Client/Cache/Items/MsalAccountCacheItem.cs
index e14bb60d72..ac739618c8 100644
--- a/src/Microsoft.Identity.Client/Cache/Items/MsalAccountCacheItem.cs
+++ b/src/Microsoft.Identity.Client/Cache/Items/MsalAccountCacheItem.cs
@@ -34,7 +34,6 @@
namespace Microsoft.Identity.Client.Cache.Items
{
- [DataContract]
internal class MsalAccountCacheItem : MsalCacheItemBase
{
internal MsalAccountCacheItem()
@@ -42,22 +41,6 @@ internal MsalAccountCacheItem()
AuthorityType = Cache.AuthorityType.MSSTS.ToString();
}
- internal MsalAccountCacheItem(string environment, MsalTokenResponse response)
- : this()
- {
- var idToken = IdToken.Parse(response.IdToken);
-
- Init(
- environment,
- idToken?.ObjectId,
- response.ClientInfo,
- idToken.Name,
- idToken.PreferredUsername,
- idToken.TenantId,
- idToken.GivenName,
- idToken.FamilyName);
- }
-
internal MsalAccountCacheItem(
string environment,
MsalTokenResponse response,
@@ -78,7 +61,7 @@ internal MsalAccountCacheItem(
idToken.FamilyName);
}
- internal MsalAccountCacheItem(
+ internal /* for test */ MsalAccountCacheItem(
string environment,
string localAccountId,
string rawClientInfo,
@@ -101,7 +84,7 @@ internal MsalAccountCacheItem(
}
internal string TenantId { get; set; }
- public string PreferredUsername { get; internal set; }
+ internal string PreferredUsername { get; set; }
internal string Name { get; set; }
internal string GivenName { get; set; }
internal string FamilyName { get; set; }
@@ -132,7 +115,7 @@ private void Init(
internal MsalAccountCacheKey GetKey()
{
- return new MsalAccountCacheKey(Environment, TenantId, HomeAccountId, PreferredUsername);
+ return new MsalAccountCacheKey(Environment, TenantId, HomeAccountId, PreferredUsername, AuthorityType);
}
internal static MsalAccountCacheItem FromJsonString(string json)
@@ -180,4 +163,4 @@ internal string ToJsonString()
.ToString();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Items/MsalAppMetadataCacheItem.cs b/src/Microsoft.Identity.Client/Cache/Items/MsalAppMetadataCacheItem.cs
new file mode 100644
index 0000000000..db7b0e0c2d
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Cache/Items/MsalAppMetadataCacheItem.cs
@@ -0,0 +1,136 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using Microsoft.Identity.Client.Cache.Keys;
+using Microsoft.Identity.Client.Utils;
+using Microsoft.Identity.Json.Linq;
+
+namespace Microsoft.Identity.Client.Cache.Items
+{
+ ///
+ /// Apps shouldn't rely on its presence, unless the app itself wrote it. It means that SDK should translate absense of app metadata to the default values of its required fields.
+ /// Other apps that don't support app metadata should never remove existing app metadata.
+ /// App metadata is a non-removable entity.It means there's no need for a public API to remove app metadata, and it shouldn't be removed when removeAccount is called.
+ /// App metadata is a non-secret entity. It means that it cannot store any secret information, like tokens, nor PII, like username etc.
+ /// App metadata can be extended by adding additional fields when required.Absense of any non-required field should translate to default values for those field.
+ ///
+ internal class MsalAppMetadataCacheItem : MsalItemWithAdditionalFields, IEquatable
+ {
+ public MsalAppMetadataCacheItem(string clientId, string env, string familyId)
+ {
+ this.ClientId = clientId;
+ this.Environment = env;
+ this.FamilyId = familyId;
+ }
+
+ /// mandatory
+ public string ClientId { get; }
+
+ /// mandatory
+
+ public string Environment { get; }
+
+ ///
+ /// The family id of which this application is part of. This is an internal feature and there is currently a single app,
+ /// with id 1. If familyId is empty, it means an app is not part of a family. A missing entry means unkown status.
+ ///
+ public string FamilyId { get; }
+
+ public MsalAppMetadataCacheKey GetKey()
+ {
+ return new MsalAppMetadataCacheKey(ClientId, Environment);
+ }
+
+ internal static MsalAppMetadataCacheItem FromJsonString(string json)
+ {
+ return FromJObject(JObject.Parse(json));
+ }
+
+ internal static MsalAppMetadataCacheItem FromJObject(JObject j)
+ {
+ string clientId = JsonUtils.ExtractExistingOrEmptyString(j, StorageJsonKeys.ClientId);
+ string environment = JsonUtils.ExtractExistingOrEmptyString(j, StorageJsonKeys.Environment);
+ string familyId = JsonUtils.ExtractExistingOrEmptyString(j, StorageJsonKeys.FamilyId);
+
+ var item = new MsalAppMetadataCacheItem(clientId, environment, familyId);
+
+ item.PopulateFieldsFromJObject(j);
+
+ return item;
+ }
+
+ internal string ToJsonString()
+ {
+ return ToJObject()
+ .ToString();
+ }
+
+ internal override JObject ToJObject()
+ {
+ var json = base.ToJObject();
+
+ json[StorageJsonKeys.Environment] = Environment;
+ json[StorageJsonKeys.ClientId] = ClientId;
+ json[StorageJsonKeys.FamilyId] = FamilyId;
+
+ return json;
+ }
+
+ #region Equals and GetHashCode
+
+ public override bool Equals(object obj)
+ {
+ return obj is MsalAppMetadataCacheItem item &&
+ Equals(item);
+
+ }
+
+ public override int GetHashCode()
+ {
+ var hashCode = -1793347351;
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ClientId);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Environment);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(FamilyId);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(AdditionalFieldsJson);
+
+ return hashCode;
+ }
+
+ public bool Equals(MsalAppMetadataCacheItem item)
+ {
+ return ClientId == item.ClientId &&
+ Environment == item.Environment &&
+ FamilyId == item.FamilyId &&
+ base.AdditionalFieldsJson == item.AdditionalFieldsJson;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Items/MsalCacheItemBase.cs b/src/Microsoft.Identity.Client/Cache/Items/MsalCacheItemBase.cs
index 4543afd2d9..e5478350a6 100644
--- a/src/Microsoft.Identity.Client/Cache/Items/MsalCacheItemBase.cs
+++ b/src/Microsoft.Identity.Client/Cache/Items/MsalCacheItemBase.cs
@@ -32,9 +32,8 @@
namespace Microsoft.Identity.Client.Cache.Items
{
- internal abstract class MsalCacheItemBase
+ internal abstract class MsalCacheItemBase : MsalItemWithAdditionalFields
{
- internal string AdditionalFieldsJson { get; set; } = "{}";
internal string HomeAccountId { get; set; }
internal string Environment { get; set; }
internal string RawClientInfo { get; set; }
@@ -48,7 +47,7 @@ internal void InitUserIdentifier()
}
}
- internal virtual void PopulateFieldsFromJObject(JObject j)
+ internal override void PopulateFieldsFromJObject(JObject j)
{
HomeAccountId = JsonUtils.ExtractExistingOrEmptyString(j, StorageJsonKeys.HomeAccountId);
Environment = JsonUtils.ExtractExistingOrEmptyString(j, StorageJsonKeys.Environment);
@@ -56,12 +55,12 @@ internal virtual void PopulateFieldsFromJObject(JObject j)
// Important: order matters. This MUST be the last one called since it will extract the
// remaining fields out.
- AdditionalFieldsJson = j.ToString();
+ base.PopulateFieldsFromJObject(j);
}
- internal virtual JObject ToJObject()
+ internal override JObject ToJObject()
{
- var json = string.IsNullOrWhiteSpace(AdditionalFieldsJson) ? new JObject() : JObject.Parse(AdditionalFieldsJson);
+ var json = base.ToJObject();
json[StorageJsonKeys.HomeAccountId] = HomeAccountId;
json[StorageJsonKeys.Environment] = Environment;
json[StorageJsonKeys.ClientInfo] = RawClientInfo;
@@ -69,4 +68,4 @@ internal virtual JObject ToJObject()
return json;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Items/MsalItemWithAdditionalFields.cs b/src/Microsoft.Identity.Client/Cache/Items/MsalItemWithAdditionalFields.cs
new file mode 100644
index 0000000000..c11f916c33
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Cache/Items/MsalItemWithAdditionalFields.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Identity.Json.Linq;
+
+namespace Microsoft.Identity.Client.Cache.Items
+{
+ internal abstract class MsalItemWithAdditionalFields
+ {
+ internal string AdditionalFieldsJson { get; set; } = "{}";
+
+ ///
+ ///Important: order matters. This MUST be the last one called since it will extract the
+ /// remaining fields out.
+ ///
+ internal virtual void PopulateFieldsFromJObject(JObject j)
+ {
+ AdditionalFieldsJson = j.ToString();
+ }
+
+
+ internal virtual JObject ToJObject()
+ {
+ var json = string.IsNullOrWhiteSpace(AdditionalFieldsJson) ? new JObject() : JObject.Parse(AdditionalFieldsJson);
+
+ return json;
+ }
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Items/MsalRefreshTokenCacheItem.cs b/src/Microsoft.Identity.Client/Cache/Items/MsalRefreshTokenCacheItem.cs
index 714e51d811..8877848e27 100644
--- a/src/Microsoft.Identity.Client/Cache/Items/MsalRefreshTokenCacheItem.cs
+++ b/src/Microsoft.Identity.Client/Cache/Items/MsalRefreshTokenCacheItem.cs
@@ -28,6 +28,7 @@
using System.Runtime.Serialization;
using Microsoft.Identity.Client.Cache.Keys;
using Microsoft.Identity.Client.OAuth2;
+using Microsoft.Identity.Client.Utils;
using Microsoft.Identity.Json.Linq;
namespace Microsoft.Identity.Client.Cache.Items
@@ -39,8 +40,11 @@ internal MsalRefreshTokenCacheItem()
CredentialType = StorageJsonValues.CredentialTypeRefreshToken;
}
- internal MsalRefreshTokenCacheItem(string environment, string clientId, MsalTokenResponse response)
- : this(environment, clientId, response.RefreshToken, response.ClientInfo)
+ internal MsalRefreshTokenCacheItem(
+ string environment,
+ string clientId,
+ MsalTokenResponse response)
+ : this(environment, clientId, response.RefreshToken, response.ClientInfo, response.FamilyId)
{
}
@@ -48,20 +52,27 @@ internal MsalRefreshTokenCacheItem(
string environment,
string clientId,
string secret,
- string rawClientInfo)
+ string rawClientInfo,
+ string familyId = null)
: this()
{
ClientId = clientId;
Environment = environment;
Secret = secret;
RawClientInfo = rawClientInfo;
+ FamilyId = familyId;
InitUserIdentifier();
}
+ ///
+ /// Optional. A value here means the token in an FRT.
+ ///
+ public string FamilyId { get; set; }
+
internal MsalRefreshTokenCacheKey GetKey()
{
- return new MsalRefreshTokenCacheKey(Environment, ClientId, HomeAccountId);
+ return new MsalRefreshTokenCacheKey(Environment, ClientId, HomeAccountId, FamilyId);
}
internal static MsalRefreshTokenCacheItem FromJsonString(string json)
@@ -72,6 +83,8 @@ internal static MsalRefreshTokenCacheItem FromJsonString(string json)
internal static MsalRefreshTokenCacheItem FromJObject(JObject j)
{
var item = new MsalRefreshTokenCacheItem();
+ item.FamilyId = JsonUtils.ExtractExistingOrEmptyString(j, StorageJsonKeys.FamilyId);
+
item.PopulateFieldsFromJObject(j);
return item;
@@ -80,6 +93,9 @@ internal static MsalRefreshTokenCacheItem FromJObject(JObject j)
internal override JObject ToJObject()
{
var json = base.ToJObject();
+
+ json[StorageJsonKeys.FamilyId] = FamilyId;
+
return json;
}
@@ -88,4 +104,4 @@ internal string ToJsonString()
return ToJObject().ToString();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Keys/IiOSKey.cs b/src/Microsoft.Identity.Client/Cache/Keys/IiOSKey.cs
new file mode 100644
index 0000000000..345de79b97
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Cache/Keys/IiOSKey.cs
@@ -0,0 +1,13 @@
+namespace Microsoft.Identity.Client.Cache.Keys
+{
+ internal interface IiOSKey
+ {
+ string iOSAccount { get; }
+
+ string iOSGeneric { get; }
+
+ string iOSService { get; }
+
+ int iOSType { get; }
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Keys/MsalAccessTokenCacheKey.cs b/src/Microsoft.Identity.Client/Cache/Keys/MsalAccessTokenCacheKey.cs
index 6d06bbf2c4..f9509a59d3 100644
--- a/src/Microsoft.Identity.Client/Cache/Keys/MsalAccessTokenCacheKey.cs
+++ b/src/Microsoft.Identity.Client/Cache/Keys/MsalAccessTokenCacheKey.cs
@@ -35,7 +35,7 @@ namespace Microsoft.Identity.Client.Cache.Keys
/// format of the key is not important for this library, as long as it is unique.
///
/// The format of the key is platform dependent
- internal class MsalAccessTokenCacheKey
+ internal class MsalAccessTokenCacheKey : IiOSKey
{
private readonly string _clientId;
private readonly string _environment;
@@ -78,53 +78,16 @@ public override string ToString()
_normalizedScopes);
}
- #region UWP
-
- ///
- /// Gets a key that is smaller than 255 characters, which is a limitation for
- /// UWP storage. This is done by hashing the scopes and env.
- ///
- ///
- /// accountId - two guids plus separator - 73 chars
- /// "accesstoken" string - 11 chars
- /// env - a sha256 string - 44 chars
- /// clientid - a guid - 36 chars
- /// tenantid - a guid - 36 chars
- /// scopes - a sha256 string - 44 chars
- /// delimiters - 4 chars
- /// total: 248 chars
- ///
- public string GetUWPFixedSizeKey(ICryptographyManager cryptographyManager)
- {
- return MsalCacheKeys.GetCredentialKey(
- _homeAccountId,
- cryptographyManager.CreateSha256Hash(_environment),
- StorageJsonValues.CredentialTypeAccessToken,
- _clientId,
- _tenantId,
- cryptographyManager.CreateSha256Hash(
- _normalizedScopes)); // can't use scopes and env because they are of variable length
- }
-
- #endregion
-
#region iOS
- public string GetiOSAccountKey()
- {
- return MsalCacheKeys.GetiOSAccountKey(_homeAccountId, _environment);
- }
+ public string iOSAccount => MsalCacheKeys.GetiOSAccountKey(_homeAccountId, _environment);
- public string GetiOSServiceKey()
- {
- return MsalCacheKeys.GetiOSServiceKey(StorageJsonValues.CredentialTypeAccessToken, _clientId, _tenantId, _normalizedScopes);
- }
+ public string iOSService => MsalCacheKeys.GetiOSServiceKey(StorageJsonValues.CredentialTypeAccessToken, _clientId, _tenantId, _normalizedScopes);
- public string GetiOSGenericKey()
- {
- return MsalCacheKeys.GetiOSGenericKey(StorageJsonValues.CredentialTypeAccessToken, _clientId, _tenantId);
- }
+ public string iOSGeneric => MsalCacheKeys.GetiOSGenericKey(StorageJsonValues.CredentialTypeAccessToken, _clientId, _tenantId);
+
+ public int iOSType => (int)MsalCacheKeys.iOSCredentialAttrType.AccessToken;
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Keys/MsalAccountCacheKey.cs b/src/Microsoft.Identity.Client/Cache/Keys/MsalAccountCacheKey.cs
index 850da9132e..545d973e4b 100644
--- a/src/Microsoft.Identity.Client/Cache/Keys/MsalAccountCacheKey.cs
+++ b/src/Microsoft.Identity.Client/Cache/Keys/MsalAccountCacheKey.cs
@@ -34,14 +34,15 @@ namespace Microsoft.Identity.Client.Cache.Keys
/// An object representing the key of the token cache Account dictionary. The
/// format of the key is not important for this library, as long as it is unique.
///
- internal class MsalAccountCacheKey
+ internal class MsalAccountCacheKey : IiOSKey
{
private readonly string _environment;
private readonly string _homeAccountId;
private readonly string _tenantId;
private readonly string _username;
+ private readonly string _authorityType;
- public MsalAccountCacheKey(string environment, string tenantId, string userIdentifier, string username)
+ public MsalAccountCacheKey(string environment, string tenantId, string userIdentifier, string username, string authorityType)
{
if (string.IsNullOrEmpty(environment))
{
@@ -52,6 +53,7 @@ public MsalAccountCacheKey(string environment, string tenantId, string userIdent
_environment = environment;
_homeAccountId = userIdentifier;
_username = username;
+ _authorityType = authorityType;
}
public override string ToString()
@@ -67,27 +69,26 @@ public override string ToString()
#region iOS
- public string GetiOSAccountKey()
+ public string iOSAccount
{
- var stringBuilder = new StringBuilder();
+ get
+ {
+ var stringBuilder = new StringBuilder();
- stringBuilder.Append(_homeAccountId ?? "");
- stringBuilder.Append(MsalCacheKeys.CacheKeyDelimiter);
+ stringBuilder.Append(_homeAccountId ?? "");
+ stringBuilder.Append(MsalCacheKeys.CacheKeyDelimiter);
- stringBuilder.Append(_environment);
+ stringBuilder.Append(_environment);
- return stringBuilder.ToString().ToLowerInvariant();
+ return stringBuilder.ToString().ToLowerInvariant();
+ }
}
- public string GetiOSServiceKey()
- {
- return (_tenantId ?? "").ToLowerInvariant();
- }
+ public string iOSGeneric => _username.ToLowerInvariant();
- public string GetiOSGenericKey()
- {
- return _username.ToLowerInvariant();
- }
+ public string iOSService => (_tenantId ?? "").ToLowerInvariant();
+
+ public int iOSType => MsalCacheKeys.iOSAuthorityTypeToAttrType[_authorityType];
#endregion
diff --git a/src/Microsoft.Identity.Client/Cache/Keys/MsalAppMetadataCacheKey.cs b/src/Microsoft.Identity.Client/Cache/Keys/MsalAppMetadataCacheKey.cs
new file mode 100644
index 0000000000..03a0fafebe
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Cache/Keys/MsalAppMetadataCacheKey.cs
@@ -0,0 +1,68 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using System;
+
+namespace Microsoft.Identity.Client.Cache.Keys
+{
+ ///
+ /// App metadata is an optional entity in cache and can be used by apps to store additional metadata applicable to a particular client.
+ ///
+ internal class MsalAppMetadataCacheKey : IiOSKey
+ {
+ private readonly string _clientId;
+ private readonly string _environment;
+
+ public MsalAppMetadataCacheKey(string clientId, string environment)
+ {
+ _clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
+ _environment = environment ?? throw new ArgumentNullException(nameof(environment));
+ }
+
+ ///
+ /// Ex: appmetadata-login.microsoftonline.com-b6c69a37-df96-4db0-9088-2ab96e1d8215
+ ///
+ ///
+ public override string ToString()
+ {
+ return ($"{StorageJsonKeys.AppMetadata}{MsalCacheKeys.CacheKeyDelimiter}" +
+ $"{_environment}{MsalCacheKeys.CacheKeyDelimiter}{_clientId}").ToLowerInvariant();
+ }
+
+ #region iOS
+
+ public string iOSService => $"{StorageJsonValues.AppMetadata}{MsalCacheKeys.CacheKeyDelimiter}{_clientId}".ToLowerInvariant();
+
+ public string iOSGeneric => "1";
+
+ public string iOSAccount => $"{_environment}".ToLowerInvariant();
+
+ public int iOSType => (int)MsalCacheKeys.iOSCredentialAttrType.AppMetadata;
+
+ #endregion
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Cache/Keys/MsalCacheKeys.cs b/src/Microsoft.Identity.Client/Cache/Keys/MsalCacheKeys.cs
index 87344faf12..686074e5b8 100644
--- a/src/Microsoft.Identity.Client/Cache/Keys/MsalCacheKeys.cs
+++ b/src/Microsoft.Identity.Client/Cache/Keys/MsalCacheKeys.cs
@@ -25,17 +25,15 @@
//
//------------------------------------------------------------------------------
+using System.Collections.Generic;
using System.Text;
namespace Microsoft.Identity.Client.Cache.Keys
{
- internal class MsalCacheKeys
+ internal partial class MsalCacheKeys
{
public const string CacheKeyDelimiter = "-";
- //public const string IdToken = "IdToken";
- //public const string AccessToken = "AccessToken";
- //public const string RefreshToken = "RefreshToken";
public static string GetCredentialKey(string homeAccountId, string environment, string keyDescriptor, string clientId, string tenantId, string scopes)
{
@@ -106,5 +104,17 @@ public static string GetiOSGenericKey(string keyDescriptor, string clientId, str
return stringBuilder.ToString().ToLowerInvariant();
}
+
+#region iOS
+
+ internal static readonly Dictionary iOSAuthorityTypeToAttrType = new Dictionary()
+ {
+ {AuthorityType.AAD.ToString(), 1001},
+ {AuthorityType.MSA.ToString(), 1002},
+ {AuthorityType.MSSTS.ToString(), 1003},
+ {AuthorityType.OTHER.ToString(), 1004},
+ };
+
+ #endregion
}
}
diff --git a/src/Microsoft.Identity.Client/Cache/Keys/MsalIdTokenCacheKey.cs b/src/Microsoft.Identity.Client/Cache/Keys/MsalIdTokenCacheKey.cs
index 5d6ce9dcf9..878a908229 100644
--- a/src/Microsoft.Identity.Client/Cache/Keys/MsalIdTokenCacheKey.cs
+++ b/src/Microsoft.Identity.Client/Cache/Keys/MsalIdTokenCacheKey.cs
@@ -33,7 +33,7 @@ namespace Microsoft.Identity.Client.Cache.Keys
/// An object representing the key of the token cache Id Token dictionary. The
/// format of the key is not important for this library, as long as it is unique.
///
- internal class MsalIdTokenCacheKey
+ internal class MsalIdTokenCacheKey : IiOSKey
{
private readonly string _environment;
private readonly string _homeAccountId;
@@ -77,20 +77,14 @@ public override string ToString()
#region iOS
- public string GetiOSAccountKey()
- {
- return MsalCacheKeys.GetiOSAccountKey(_homeAccountId, _environment);
- }
- public string GetiOSServiceKey()
- {
- return MsalCacheKeys.GetiOSServiceKey(StorageJsonValues.CredentialTypeIdToken, _clientId, _tenantId, scopes: null);
- }
+ public string iOSAccount => MsalCacheKeys.GetiOSAccountKey(_homeAccountId, _environment);
- public string GetiOSGenericKey()
- {
- return MsalCacheKeys.GetiOSGenericKey(StorageJsonValues.CredentialTypeIdToken, _clientId, _tenantId);
- }
+ public string iOSGeneric => MsalCacheKeys.GetiOSGenericKey(StorageJsonValues.CredentialTypeIdToken, _clientId, _tenantId);
+
+ public string iOSService => MsalCacheKeys.GetiOSServiceKey(StorageJsonValues.CredentialTypeIdToken, _clientId, _tenantId, scopes: null);
+
+ public int iOSType => (int)MsalCacheKeys.iOSCredentialAttrType.IdToken;
#endregion
}
diff --git a/src/Microsoft.Identity.Client/Cache/Keys/MsalRefreshTokenCacheKey.cs b/src/Microsoft.Identity.Client/Cache/Keys/MsalRefreshTokenCacheKey.cs
index 3adc5f3bc9..8d27680721 100644
--- a/src/Microsoft.Identity.Client/Cache/Keys/MsalRefreshTokenCacheKey.cs
+++ b/src/Microsoft.Identity.Client/Cache/Keys/MsalRefreshTokenCacheKey.cs
@@ -33,13 +33,25 @@ namespace Microsoft.Identity.Client.Cache.Keys
/// An object representing the key of the token cache RT dictionary. The
/// format of the key is not important for this library, as long as it is unique.
///
- internal class MsalRefreshTokenCacheKey
+ ///
+ /// Normal RTs are scoped by env, account_id and clientID
+ /// FRTs are scoped by env, account_id and familyID (clientID exists, but is irrelevant)
+ ///
+ internal class MsalRefreshTokenCacheKey : IiOSKey //TODO bogavril: add a base class with FRT key?
{
private readonly string _environment;
private readonly string _homeAccountId;
private readonly string _clientId;
+ private readonly string _familyId;
- internal MsalRefreshTokenCacheKey(string environment, string clientId, string userIdentifier)
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ /// Can be null or empty, denoting a normal RT. A value signifies an FRT.
+ internal MsalRefreshTokenCacheKey(string environment, string clientId, string userIdentifier, string familyId)
{
if (string.IsNullOrEmpty(environment))
{
@@ -54,36 +66,71 @@ internal MsalRefreshTokenCacheKey(string environment, string clientId, string us
_environment = environment;
_homeAccountId = userIdentifier;
_clientId = clientId;
+ _familyId = familyId;
}
public override string ToString()
{
+ // FRT
+ if (!String.IsNullOrWhiteSpace(_familyId))
+ {
+ string d = MsalCacheKeys.CacheKeyDelimiter;
+ return $"{_homeAccountId}{d}{_environment}{d}{StorageJsonValues.CredentialTypeRefreshToken}{d}{_familyId}{d}{d}".ToLowerInvariant();
+
+ }
+
return MsalCacheKeys.GetCredentialKey(
- _homeAccountId,
- _environment,
- StorageJsonValues.CredentialTypeRefreshToken,
- _clientId,
- tenantId: null,
- scopes: null);
+ _homeAccountId,
+ _environment,
+ StorageJsonValues.CredentialTypeRefreshToken,
+ _clientId,
+ tenantId: null,
+ scopes: null);
}
#region iOS
- public string GetiOSAccountKey()
+ public string iOSAccount
{
- return MsalCacheKeys.GetiOSAccountKey(_homeAccountId, _environment);
+ get
+ {
+ return MsalCacheKeys.GetiOSAccountKey(_homeAccountId, _environment);
+ }
}
- public string GetiOSServiceKey()
+ public string iOSGeneric
{
- return MsalCacheKeys.GetiOSServiceKey(StorageJsonValues.CredentialTypeRefreshToken, _clientId, tenantId: null, scopes: null);
+ get
+ {
+ // FRT
+ if (!String.IsNullOrWhiteSpace(_familyId))
+ {
+ return $"{StorageJsonValues.CredentialTypeRefreshToken}{MsalCacheKeys.CacheKeyDelimiter}{_familyId}{MsalCacheKeys.CacheKeyDelimiter}".ToLowerInvariant();
+ }
+
+ return MsalCacheKeys.GetiOSGenericKey(StorageJsonValues.CredentialTypeRefreshToken, _clientId, tenantId: null);
+
+ }
}
- public string GetiOSGenericKey()
+ public string iOSService
{
- return MsalCacheKeys.GetiOSGenericKey(StorageJsonValues.CredentialTypeRefreshToken, _clientId, tenantId: null);
+ get
+ {
+ // FRT
+ if (!String.IsNullOrWhiteSpace(_familyId))
+ {
+ return $"{StorageJsonValues.CredentialTypeRefreshToken}{MsalCacheKeys.CacheKeyDelimiter}{_familyId}{MsalCacheKeys.CacheKeyDelimiter}{MsalCacheKeys.CacheKeyDelimiter}".ToLowerInvariant();
+ }
+
+ return MsalCacheKeys.GetiOSServiceKey(StorageJsonValues.CredentialTypeRefreshToken, _clientId, tenantId: null, scopes: null);
+ }
}
+ public int iOSType => (int)MsalCacheKeys.iOSCredentialAttrType.RefreshToken;
+
+
+
#endregion
}
}
diff --git a/src/Microsoft.Identity.Client/Cache/Keys/iOSCredentialAttrType.cs b/src/Microsoft.Identity.Client/Cache/Keys/iOSCredentialAttrType.cs
new file mode 100644
index 0000000000..f6651a6521
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Cache/Keys/iOSCredentialAttrType.cs
@@ -0,0 +1,45 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+namespace Microsoft.Identity.Client.Cache.Keys
+{
+ internal partial class MsalCacheKeys
+ {
+ #region iOS
+
+ internal enum iOSCredentialAttrType
+ {
+ AccessToken = 2001,
+ RefreshToken = 2002,
+ IdToken = 2003,
+ Password = 2004,
+ AppMetadata = 3001
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Cache/StorageJsonKeys.cs b/src/Microsoft.Identity.Client/Cache/StorageJsonKeys.cs
index f586688fb4..1e86cbdec8 100644
--- a/src/Microsoft.Identity.Client/Cache/StorageJsonKeys.cs
+++ b/src/Microsoft.Identity.Client/Cache/StorageJsonKeys.cs
@@ -50,6 +50,8 @@ internal static class StorageJsonKeys
public const string ExtendedExpiresOn = "extended_expires_on";
public const string ClientInfo = "client_info";
public const string FamilyId = "family_id";
+ public const string AppMetadata = "appmetadata";
+
// todo(cache): this needs to be added to the spec. needed for OBO flow on .NET.
public const string UserAssertionHash = "user_assertion_hash";
@@ -58,4 +60,4 @@ internal static class StorageJsonKeys
// this is here for back compat
public const string ExtendedExpiresOn_MsalCompat = "ext_expires_on";
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/StorageJsonValues.cs b/src/Microsoft.Identity.Client/Cache/StorageJsonValues.cs
index 4c0e6db6e6..154e08b3b2 100644
--- a/src/Microsoft.Identity.Client/Cache/StorageJsonValues.cs
+++ b/src/Microsoft.Identity.Client/Cache/StorageJsonValues.cs
@@ -38,5 +38,7 @@ internal static class StorageJsonValues
public const string CredentialTypeIdToken = "IdToken";
public const string AccountRootKey = "Account";
public const string CredentialTypeOther = "Other";
+
+ public const string AppMetadata = "AppMetadata";
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Cache/TokenCacheJsonSerializer.cs b/src/Microsoft.Identity.Client/Cache/TokenCacheJsonSerializer.cs
index 54b3c55b65..540a381897 100644
--- a/src/Microsoft.Identity.Client/Cache/TokenCacheJsonSerializer.cs
+++ b/src/Microsoft.Identity.Client/Cache/TokenCacheJsonSerializer.cs
@@ -68,6 +68,12 @@ public byte[] Serialize()
.ToString()] = accountItem;
}
+ foreach (var appMetadata in _accessor.GetAllAppMetadata())
+ {
+ cache.AppMetadata[appMetadata.GetKey()
+ .ToString()] = appMetadata;
+ }
+
return cache.ToJsonString()
.ToByteArray();
}
@@ -116,6 +122,14 @@ public void Deserialize(byte[] bytes)
_accessor.SaveAccount(account);
}
}
+
+ if (cache.AppMetadata != null)
+ {
+ foreach (var appMetadata in cache.AppMetadata.Values)
+ {
+ _accessor.SaveAppMetadata(appMetadata);
+ }
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/ITokenCacheInternal.cs b/src/Microsoft.Identity.Client/ITokenCacheInternal.cs
index f5870e1415..81ccfe4aa6 100644
--- a/src/Microsoft.Identity.Client/ITokenCacheInternal.cs
+++ b/src/Microsoft.Identity.Client/ITokenCacheInternal.cs
@@ -44,13 +44,23 @@ internal interface ITokenCacheInternal : ITokenCache
void RemoveAccount(IAccount account, RequestContext requestContext);
IEnumerable GetAccounts(string authority);
- Tuple SaveAccessAndRefreshToken(
+ ///
+ /// Persists the AT and RT and updates app metadata (FOCI)
+ ///
+ ///
+ Tuple SaveTokenResponse(
AuthenticationRequestParameters authenticationRequestParameters,
MsalTokenResponse msalTokenResponse);
Task FindAccessTokenAsync(AuthenticationRequestParameters authenticationRequestParameters);
MsalIdTokenCacheItem GetIdTokenCacheItem(MsalIdTokenCacheKey getIdTokenItemKey, RequestContext requestContext);
- Task FindRefreshTokenAsync(AuthenticationRequestParameters authenticationRequestParameters);
+
+ ///
+ /// Returns a RT for the request. If familyId is specified, it tries to return the FRT.
+ ///
+ Task FindRefreshTokenAsync(
+ AuthenticationRequestParameters authenticationRequestParameters,
+ string familyId = null);
void SetIosKeychainSecurityGroup(string securityGroup);
@@ -64,8 +74,14 @@ Tuple SaveAccessAndRefreshToken(
IEnumerable GetAllIdTokens(bool filterByClientId);
IEnumerable GetAllAccounts();
+ ///
+ /// FOCI - check in the app metadata to see if the app is part of the family
+ ///
+ /// null if unkown, true or false if app metadata has details
+ Task IsFociMemberAsync(AuthenticationRequestParameters authenticationRequestParameters, string familyId);
+
void ClearAdalCache();
void ClearMsalCache();
void Clear();
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Internal/Requests/InteractiveRequest.cs b/src/Microsoft.Identity.Client/Internal/Requests/InteractiveRequest.cs
index 0e83273644..57fe0bdeec 100644
--- a/src/Microsoft.Identity.Client/Internal/Requests/InteractiveRequest.cs
+++ b/src/Microsoft.Identity.Client/Internal/Requests/InteractiveRequest.cs
@@ -92,15 +92,18 @@ internal override async Task ExecuteAsync(CancellationToke
await AcquireAuthorizationAsync(cancellationToken).ConfigureAwait(false);
VerifyAuthorizationResult();
- BrokerInteractiveRequest brokerInteractiveRequest = new BrokerInteractiveRequest(
- AuthenticationRequestParameters,
- _interactiveParameters,
- ServiceBundle,
- _authorizationResult);
-
- if (AuthenticationRequestParameters.IsBrokerEnabled || brokerInteractiveRequest.IsBrokerInvocationRequired())
+ if (AuthenticationRequestParameters.IsBrokerEnabled)
{
- _msalTokenResponse = await brokerInteractiveRequest.SendTokenRequestToBrokerAsync().ConfigureAwait(false);
+ var brokerInteractiveRequest = new BrokerInteractiveRequest(
+ AuthenticationRequestParameters,
+ _interactiveParameters,
+ ServiceBundle,
+ _authorizationResult);
+
+ if (brokerInteractiveRequest.IsBrokerInvocationRequired())
+ {
+ _msalTokenResponse = await brokerInteractiveRequest.SendTokenRequestToBrokerAsync().ConfigureAwait(false);
+ }
}
else
{
@@ -255,7 +258,7 @@ private Dictionary CreateAuthorizationRequestParameters()
private void VerifyAuthorizationResult()
{
- if (_authorizationResult.Status == AuthorizationStatus.Success &&
+ if (_authorizationResult.Status == AuthorizationStatus.Success &&
!_state.Equals(_authorizationResult.State,
StringComparison.OrdinalIgnoreCase))
{
@@ -286,4 +289,4 @@ private void VerifyAuthorizationResult()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs b/src/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs
index 9466e05ab6..b0e7b6c1d2 100644
--- a/src/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs
+++ b/src/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs
@@ -216,7 +216,7 @@ protected AuthenticationResult CacheTokenResponseAndCreateAuthenticationResult(M
{
AuthenticationRequestParameters.RequestContext.Logger.Info("Saving Token Response to cache..");
- var tuple = CacheManager.SaveAccessAndRefreshToken(msalTokenResponse);
+ var tuple = CacheManager.SaveTokenResponse(msalTokenResponse);
return new AuthenticationResult(tuple.Item1, tuple.Item2);
}
else
diff --git a/src/Microsoft.Identity.Client/Internal/Requests/SilentRequest.cs b/src/Microsoft.Identity.Client/Internal/Requests/SilentRequest.cs
index b397c8808e..673047a484 100644
--- a/src/Microsoft.Identity.Client/Internal/Requests/SilentRequest.cs
+++ b/src/Microsoft.Identity.Client/Internal/Requests/SilentRequest.cs
@@ -34,12 +34,14 @@
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.TelemetryCore;
using Microsoft.Identity.Client.Internal.Broker;
+using System;
namespace Microsoft.Identity.Client.Internal.Requests
{
internal class SilentRequest : RequestBase
{
private readonly AcquireTokenSilentParameters _silentParameters;
+ private const string TheOnlyFamilyId = "1";
public SilentRequest(
IServiceBundle serviceBundle,
@@ -59,33 +61,84 @@ internal override async Task ExecuteAsync(CancellationToke
"Token cache is set to null. Silent requests cannot be executed.");
}
- MsalAccessTokenCacheItem msalAccessTokenItem = null;
-
// Look for access token
if (!_silentParameters.ForceRefresh)
{
- msalAccessTokenItem =
+ MsalAccessTokenCacheItem msalAccessTokenItem =
await CacheManager.FindAccessTokenAsync().ConfigureAwait(false);
+
+ if (msalAccessTokenItem != null)
+ {
+ var msalIdTokenItem = CacheManager.GetIdTokenCacheItem(msalAccessTokenItem.GetIdTokenItemKey());
+ return new AuthenticationResult(msalAccessTokenItem, msalIdTokenItem);
+ }
}
- if (msalAccessTokenItem != null)
+ // Try FOCI first
+ MsalTokenResponse msalTokenResponse = await TryGetTokenUsingFociAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ // Normal, non-FOCI flow
+ if (msalTokenResponse == null)
{
- var msalIdTokenItem = CacheManager.GetIdTokenCacheItem(msalAccessTokenItem.GetIdTokenItemKey());
+ // Look for a refresh token
+ MsalRefreshTokenCacheItem appRefreshToken = await FindRefreshTokenOrFailAsync()
+ .ConfigureAwait(false);
- return new AuthenticationResult(msalAccessTokenItem, msalIdTokenItem);
+ msalTokenResponse = await RefreshAccessTokenAsync(appRefreshToken, cancellationToken)
+ .ConfigureAwait(false);
}
+ return CacheTokenResponseAndCreateAuthenticationResult(msalTokenResponse);
+ }
- var msalRefreshTokenItem = await CacheManager.FindRefreshTokenAsync().ConfigureAwait(false);
-
- if (msalRefreshTokenItem == null)
+ private async Task TryGetTokenUsingFociAsync(CancellationToken cancellationToken)
+ {
+ if (!ServiceBundle.PlatformProxy.GetFeatureFlags().IsFociEnabled)
{
- AuthenticationRequestParameters.RequestContext.Logger.Verbose("No Refresh Token was found in the cache");
+ return null;
+ }
- throw new MsalUiRequiredException(
- MsalUiRequiredException.NoTokensFoundError,
- "No Refresh Token found in the cache");
+ var logger = AuthenticationRequestParameters.RequestContext.Logger;
+
+ // If the app was just added to the family, the app metadata will reflect this
+ // after the first RT exchanged.
+ bool? isFamilyMember = await CacheManager.IsAppFociMemberAsync(TheOnlyFamilyId).ConfigureAwait(false);
+
+ if (isFamilyMember.HasValue && isFamilyMember.Value == false)
+ {
+ AuthenticationRequestParameters.RequestContext.Logger.Verbose(
+ "[FOCI] App is not part of the family, skipping FOCI.");
+
+ return null;
+ }
+
+ logger.Verbose("[FOCI] App is part of the family or unkown, looking for FRT");
+ var familyRefreshToken = await CacheManager.FindFamilyRefreshTokenAsync(TheOnlyFamilyId).ConfigureAwait(false);
+ logger.Verbose("[FOCI] FRT found? " + (familyRefreshToken != null));
+
+ if (familyRefreshToken != null)
+ {
+ try
+ {
+ MsalTokenResponse frtTokenResponse = await RefreshAccessTokenAsync(familyRefreshToken, cancellationToken)
+ .ConfigureAwait(false);
+
+ logger.Verbose("[FOCI] FRT exchanged succeeded");
+ return frtTokenResponse;
+ }
+ catch (MsalServiceException)
+ {
+ logger.Error("[FOCI] FRT exchanged failed " + (familyRefreshToken != null));
+ return null;
+ }
}
+ return null;
+
+ }
+
+ private async Task RefreshAccessTokenAsync(MsalRefreshTokenCacheItem msalRefreshTokenItem, CancellationToken cancellationToken)
+ {
AuthenticationRequestParameters.RequestContext.Logger.Verbose("Refreshing access token...");
await ResolveAuthorityEndpointsAsync().ConfigureAwait(false);
@@ -99,7 +152,22 @@ internal override async Task ExecuteAsync(CancellationToke
"Refresh token was missing from the token refresh response, so the refresh token in the request is returned instead");
}
- return CacheTokenResponseAndCreateAuthenticationResult(msalTokenResponse);
+ return msalTokenResponse;
+ }
+
+ private async Task FindRefreshTokenOrFailAsync()
+ {
+ var msalRefreshTokenItem = await CacheManager.FindRefreshTokenAsync().ConfigureAwait(false);
+ if (msalRefreshTokenItem == null)
+ {
+ AuthenticationRequestParameters.RequestContext.Logger.Verbose("No Refresh Token was found in the cache");
+
+ throw new MsalUiRequiredException(
+ MsalUiRequiredException.NoTokensFoundError,
+ "No Refresh Token found in the cache");
+ }
+
+ return msalRefreshTokenItem;
}
protected override void EnrichTelemetryApiEvent(ApiEvent apiEvent)
@@ -121,4 +189,4 @@ private Dictionary GetBodyParameters(string refreshTokenSecret)
return dict;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj b/src/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj
index 276754ccb0..8455fd4077 100644
--- a/src/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj
+++ b/src/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj
@@ -24,7 +24,4 @@
-
-
-
\ No newline at end of file
diff --git a/src/Microsoft.Identity.Client/OAuth2/MsalTokenResponse.cs b/src/Microsoft.Identity.Client/OAuth2/MsalTokenResponse.cs
index 195fa8f959..de814ac078 100644
--- a/src/Microsoft.Identity.Client/OAuth2/MsalTokenResponse.cs
+++ b/src/Microsoft.Identity.Client/OAuth2/MsalTokenResponse.cs
@@ -48,6 +48,7 @@ internal class TokenResponseClaim : OAuth2ResponseBaseClaim
public const string CreatedOn = "created_on";
public const string ExtendedExpiresIn = "ext_expires_in";
public const string Authority = "authority";
+ public const string FamilyId = "foci";
}
[DataContract]
@@ -96,6 +97,12 @@ public long ExtendedExpiresIn
}
}
+ ///
+ /// Optional field, FOCI support.
+ ///
+ [DataMember(Name=TokenResponseClaim.FamilyId, IsRequired = false)]
+ public string FamilyId { get; set; }
+
public DateTimeOffset AccessTokenExpiresOn { get; private set; }
public DateTimeOffset AccessTokenExtendedExpiresOn { get; private set; }
@@ -134,4 +141,4 @@ internal static MsalTokenResponse CreateFromBrokerResponse(Dictionary
- /// The main entry point for the application.
+ /// FOCI is not currently supported on Android because app metadata serialization is not defined.
///
- [STAThread]
- static void Main()
- {
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
- Application.Run(new MainForm());
- }
+ public bool IsFociEnabled => false;
}
}
diff --git a/src/Microsoft.Identity.Client/Platforms/Android/AndroidPlatformProxy.cs b/src/Microsoft.Identity.Client/Platforms/Android/AndroidPlatformProxy.cs
index d753df5a99..8a8e0d34d8 100644
--- a/src/Microsoft.Identity.Client/Platforms/Android/AndroidPlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/Platforms/Android/AndroidPlatformProxy.cs
@@ -165,5 +165,7 @@ protected override IWebUIFactory CreateWebUiFactory()
protected override ICryptographyManager InternalGetCryptographyManager() => new AndroidCryptographyManager();
protected override IPlatformLogger InternalGetPlatformLogger() => new AndroidPlatformLogger();
+
+ protected override IFeatureFlags CreateFeatureFlags() => new AndroidFeatureFlags();
}
}
diff --git a/src/Microsoft.Identity.Client/Platforms/Android/AndroidTokenCacheAccessor.cs b/src/Microsoft.Identity.Client/Platforms/Android/AndroidTokenCacheAccessor.cs
index 17775602d7..2842b91104 100644
--- a/src/Microsoft.Identity.Client/Platforms/Android/AndroidTokenCacheAccessor.cs
+++ b/src/Microsoft.Identity.Client/Platforms/Android/AndroidTokenCacheAccessor.cs
@@ -137,22 +137,22 @@ private void DeleteAll(ISharedPreferences sharedPreferences)
editor.Apply();
}
- public ICollection GetAllAccessTokens()
+ public IEnumerable GetAllAccessTokens()
{
return _accessTokenSharedPreference.All.Values.Cast().Select(x => MsalAccessTokenCacheItem.FromJsonString(x)).ToList();
}
- public ICollection GetAllRefreshTokens()
+ public IEnumerable GetAllRefreshTokens()
{
return _refreshTokenSharedPreference.All.Values.Cast().Select(x => MsalRefreshTokenCacheItem.FromJsonString(x)).ToList();
}
- public ICollection GetAllIdTokens()
+ public IEnumerable GetAllIdTokens()
{
return _idTokenSharedPreference.All.Values.Cast().Select(x => MsalIdTokenCacheItem.FromJsonString(x)).ToList();
}
- public ICollection GetAllAccounts()
+ public IEnumerable GetAllAccounts()
{
return _accountSharedPreference.All.Values.Cast().Select(x => MsalAccountCacheItem.FromJsonString(x)).ToList();
}
@@ -185,28 +185,31 @@ public MsalAccountCacheItem GetAccount(MsalAccountCacheKey accountKey)
return MsalAccountCacheItem.FromJsonString(_accountSharedPreference.GetString(accountKey.ToString(), null));
}
- ///
- public int RefreshTokenCount => throw new NotImplementedException();
-
- ///
- public int AccessTokenCount => throw new NotImplementedException();
+ #region App Metadata - not used on Android
+ public MsalAppMetadataCacheItem ReadAppMetadata(MsalAppMetadataCacheKey appMetadataKey)
+ {
+ throw new NotImplementedException();
+ }
- ///
- public int AccountCount => throw new NotImplementedException();
+ public void WriteAppMetadata(MsalAppMetadataCacheItem appMetadata)
+ {
+ throw new NotImplementedException();
+ }
- ///
- public int IdTokenCount => throw new NotImplementedException();
+ public void SaveAppMetadata(MsalAppMetadataCacheItem item)
+ {
+ throw new NotImplementedException();
+ }
- ///
- public void ClearRefreshTokens()
+ public IEnumerable GetAllAppMetadata()
{
throw new NotImplementedException();
}
- ///
- public void ClearAccessTokens()
+ public MsalAppMetadataCacheItem GetAppMetadata(MsalAppMetadataCacheKey appMetadataKey)
{
throw new NotImplementedException();
}
+ #endregion
}
}
diff --git a/src/Microsoft.Identity.Client/Platforms/Mac/MacFeatureFlags.cs b/src/Microsoft.Identity.Client/Platforms/Mac/MacFeatureFlags.cs
new file mode 100644
index 0000000000..c4fb2bccec
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Platforms/Mac/MacFeatureFlags.cs
@@ -0,0 +1,36 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
+
+namespace Microsoft.Identity.Client.Platforms.Mac
+{
+ internal class MacFeatureFlags : IFeatureFlags
+ {
+ public bool IsFociEnabled => true;
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/Mac/MacPlatformProxy.cs b/src/Microsoft.Identity.Client/Platforms/Mac/MacPlatformProxy.cs
index 6b52a5d350..fc98579a76 100644
--- a/src/Microsoft.Identity.Client/Platforms/Mac/MacPlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/Platforms/Mac/MacPlatformProxy.cs
@@ -161,5 +161,7 @@ public override ITokenCacheAccessor CreateTokenCacheAccessor()
protected override IWebUIFactory CreateWebUiFactory() => new MacUIFactory();
protected override ICryptographyManager InternalGetCryptographyManager() => new MacCryptographyManager();
protected override IPlatformLogger InternalGetPlatformLogger() => new ConsolePlatformLogger();
+
+ protected override IFeatureFlags CreateFeatureFlags() => new MacFeatureFlags();
}
}
diff --git a/src/Microsoft.Identity.Client/Platforms/iOS/iOSFeatureFlags.cs b/src/Microsoft.Identity.Client/Platforms/iOS/iOSFeatureFlags.cs
new file mode 100644
index 0000000000..bfcbc5f8d3
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Platforms/iOS/iOSFeatureFlags.cs
@@ -0,0 +1,39 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
+
+namespace Microsoft.Identity.Client.Platforms.iOS
+{
+ internal class iOSFeatureFlags : IFeatureFlags
+ {
+ ///
+ /// FOCI has not been tested on iOS
+ ///
+ public bool IsFociEnabled => false;
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/iOS/iOSPlatformProxy.cs b/src/Microsoft.Identity.Client/Platforms/iOS/iOSPlatformProxy.cs
index fce015fc97..02c626c945 100644
--- a/src/Microsoft.Identity.Client/Platforms/iOS/iOSPlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/Platforms/iOS/iOSPlatformProxy.cs
@@ -151,5 +151,7 @@ protected override IWebUIFactory CreateWebUiFactory()
protected override ICryptographyManager InternalGetCryptographyManager() => new iOSCryptographyManager();
protected override IPlatformLogger InternalGetPlatformLogger() => new ConsolePlatformLogger();
+
+ protected override IFeatureFlags CreateFeatureFlags() => new iOSFeatureFlags();
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/iOS/iOSTokenCacheAccessor.cs b/src/Microsoft.Identity.Client/Platforms/iOS/iOSTokenCacheAccessor.cs
index 103e2303c7..0b09394f4d 100644
--- a/src/Microsoft.Identity.Client/Platforms/iOS/iOSTokenCacheAccessor.cs
+++ b/src/Microsoft.Identity.Client/Platforms/iOS/iOSTokenCacheAccessor.cs
@@ -35,7 +35,6 @@
using Microsoft.Identity.Client.Cache.Keys;
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.Exceptions;
-using Microsoft.Identity.Client.Utils;
using Security;
namespace Microsoft.Identity.Client.Platforms.iOS
@@ -43,21 +42,7 @@ namespace Microsoft.Identity.Client.Platforms.iOS
internal class iOSTokenCacheAccessor : ITokenCacheAccessor
{
public const string CacheKeyDelimiter = "-";
- private static readonly Dictionary AuthorityTypeToAttrType = new Dictionary()
- {
- {AuthorityType.AAD.ToString(), 1001},
- {AuthorityType.MSA.ToString(), 1002},
- {AuthorityType.MSSTS.ToString(), 1003},
- {AuthorityType.OTHER.ToString(), 1004},
- };
- private enum CredentialAttrType
- {
- AccessToken = 2001,
- RefreshToken = 2002,
- IdToken = 2003,
- Password = 2004
- }
private const bool _defaultSyncSetting = false;
private const SecAccessible _defaultAccessiblityPolicy = SecAccessible.AfterFirstUnlockThisDeviceOnly;
@@ -66,7 +51,7 @@ private enum CredentialAttrType
// Identifier for the keychain item used to retrieve current team ID
private const string TeamIdKey = "DotNetTeamIDHint";
- private string keychainGroup;
+ private string _keychainGroup;
private readonly RequestContext _requestContext;
private string GetBundleId()
@@ -78,11 +63,11 @@ public void SetiOSKeychainSecurityGroup(string keychainSecurityGroup)
{
if (keychainSecurityGroup == null)
{
- keychainGroup = GetBundleId();
+ _keychainGroup = GetBundleId();
}
else
{
- keychainGroup = GetTeamId() + '.' + keychainSecurityGroup;
+ _keychainGroup = GetTeamId() + '.' + keychainSecurityGroup;
}
}
@@ -115,7 +100,7 @@ private string GetTeamId()
public iOSTokenCacheAccessor()
{
- keychainGroup = GetTeamId() + '.' + DefaultKeychainGroup;
+ _keychainGroup = GetTeamId() + '.' + DefaultKeychainGroup;
}
public iOSTokenCacheAccessor(RequestContext requestContext) : this()
@@ -125,127 +110,81 @@ public iOSTokenCacheAccessor(RequestContext requestContext) : this()
public void SaveAccessToken(MsalAccessTokenCacheItem item)
{
- var key = item.GetKey();
-
- var account = key.GetiOSAccountKey();
- var service = key.GetiOSServiceKey();
- var generic = key.GetiOSGenericKey();
- var type = (int)CredentialAttrType.AccessToken;
-
- var value = item.ToJsonString();
-
- Save(account, service, generic, type, value);
+ IiOSKey key = item.GetKey();
+ Save(key, item.ToJsonString());
}
public void SaveRefreshToken(MsalRefreshTokenCacheItem item)
{
- var key = item.GetKey();
- var account = key.GetiOSAccountKey();
- var service = key.GetiOSServiceKey();
- var generic = key.GetiOSGenericKey();
-
- var type = (int)CredentialAttrType.RefreshToken;
-
- var value = item.ToJsonString();
-
- Save(account, service, generic, type, value);
+ Save(item.GetKey(), item.ToJsonString());
}
public void SaveIdToken(MsalIdTokenCacheItem item)
{
- var key = item.GetKey();
- var account = key.GetiOSAccountKey();
- var service = key.GetiOSServiceKey();
- var generic = key.GetiOSGenericKey();
-
- var type = (int)CredentialAttrType.IdToken;
-
- var value = item.ToJsonString();
-
- Save(account, service, generic, type, value);
+ Save(item.GetKey(), item.ToJsonString());
}
public void SaveAccount(MsalAccountCacheItem item)
{
- var key = item.GetKey();
- var account = key.GetiOSAccountKey();
- var service = key.GetiOSServiceKey();
- var generic = key.GetiOSGenericKey();
-
- var type = AuthorityTypeToAttrType[item.AuthorityType];
-
- var value = item.ToJsonString();
-
- Save(account, service, generic, type, value);
+ Save(item.GetKey(), item.ToJsonString());
}
public void DeleteAccessToken(MsalAccessTokenCacheKey cacheKey)
{
- var account = cacheKey.GetiOSAccountKey();
- var service = cacheKey.GetiOSServiceKey();
-
- var type = (int)CredentialAttrType.AccessToken;
-
- Remove(account, service, type);
+ Remove(cacheKey);
}
public void DeleteRefreshToken(MsalRefreshTokenCacheKey cacheKey)
{
- var account = cacheKey.GetiOSAccountKey();
- var service = cacheKey.GetiOSServiceKey();
-
- var type = (int)CredentialAttrType.RefreshToken;
-
- Remove(account, service, type);
+ Remove(cacheKey);
}
public void DeleteIdToken(MsalIdTokenCacheKey cacheKey)
{
- var account = cacheKey.GetiOSAccountKey();
- var service = cacheKey.GetiOSServiceKey();
-
- var type = (int)CredentialAttrType.IdToken;
-
- Remove(account, service, type);
+ Remove(cacheKey);
}
public void DeleteAccount(MsalAccountCacheKey cacheKey)
{
- var account = cacheKey.GetiOSAccountKey();
- var service = cacheKey.GetiOSServiceKey();
-
- var type = AuthorityTypeToAttrType[AuthorityType.MSSTS.ToString()];
-
- Remove(account, service, type);
+ Remove(cacheKey);
}
- public ICollection GetAllAccessTokens()
+
+ public IEnumerable GetAllAccessTokens()
{
- return GetValues((int)CredentialAttrType.AccessToken).Select(x => MsalAccessTokenCacheItem.FromJsonString(x)).ToList();
+ return GetPayloadAsString((int)MsalCacheKeys.iOSCredentialAttrType.AccessToken)
+ .Select(x => MsalAccessTokenCacheItem.FromJsonString(x))
+ .ToList();
}
- public ICollection GetAllRefreshTokens()
+ public IEnumerable GetAllRefreshTokens()
{
- return GetValues((int)CredentialAttrType.RefreshToken).Select(x => MsalRefreshTokenCacheItem.FromJsonString(x)).ToList();
+ return GetPayloadAsString((int)MsalCacheKeys.iOSCredentialAttrType.RefreshToken)
+ .Select(x => MsalRefreshTokenCacheItem.FromJsonString(x))
+ .ToList();
}
- public ICollection GetAllIdTokens()
+ public IEnumerable GetAllIdTokens()
{
- return GetValues((int)CredentialAttrType.IdToken).Select(x => MsalIdTokenCacheItem.FromJsonString(x)).ToList();
+ return GetPayloadAsString((int)MsalCacheKeys.iOSCredentialAttrType.IdToken)
+ .Select(x => MsalIdTokenCacheItem.FromJsonString(x))
+ .ToList();
}
- public ICollection GetAllAccounts()
+ public IEnumerable GetAllAccounts()
{
- return GetValues(AuthorityTypeToAttrType[AuthorityType.MSSTS.ToString()]).Select(x => MsalAccountCacheItem.FromJsonString(x)).ToList();
+ return GetPayloadAsString(MsalCacheKeys.iOSAuthorityTypeToAttrType[AuthorityType.MSSTS.ToString()])
+ .Select(x => MsalAccountCacheItem.FromJsonString(x))
+ .ToList();
}
- private string GetValue(string account, string service, int type)
+ private string GetPayload(IiOSKey key)
{
var queryRecord = new SecRecord(SecKind.GenericPassword)
{
- Account = account,
- Service = service,
- CreatorType = type,
- AccessGroup = keychainGroup
+ Account = key.iOSAccount,
+ Service = key.iOSService,
+ CreatorType = key.iOSType,
+ AccessGroup = _keychainGroup
};
var match = SecKeyChain.QueryAsRecord(queryRecord, out SecStatusCode resultCode);
@@ -255,15 +194,15 @@ private string GetValue(string account, string service, int type)
: string.Empty;
}
- private ICollection GetValues(int type)
+ private ICollection GetPayloadAsString(int type)
{
var queryRecord = new SecRecord(SecKind.GenericPassword)
{
CreatorType = type,
- AccessGroup = keychainGroup
+ AccessGroup = _keychainGroup
};
- SecRecord[] records = SecKeyChain.QueryAsRecord(queryRecord, Int32.MaxValue, out SecStatusCode resultCode);
+ SecRecord[] records = SecKeyChain.QueryAsRecord(queryRecord, int.MaxValue, out SecStatusCode resultCode);
ICollection res = new List();
@@ -279,9 +218,19 @@ private ICollection GetValues(int type)
return res;
}
- private SecStatusCode Save(string account, string service, string generic, int type, string value)
+ private SecStatusCode Save(IiOSKey key, string payload)
{
- SecRecord recordToSave = CreateRecord(account, service, generic, type, value);
+ var recordToSave = new SecRecord(SecKind.GenericPassword)
+ {
+ Account = key.iOSAccount,
+ Service = key.iOSService,
+ Generic = key.iOSGeneric,
+ CreatorType = key.iOSType,
+ ValueData = NSData.FromString(payload, NSStringEncoding.UTF8),
+ AccessGroup = _keychainGroup,
+ Accessible = _defaultAccessiblityPolicy,
+ Synchronizable = _defaultSyncSetting,
+ };
var secStatusCode = Update(recordToSave);
@@ -303,29 +252,15 @@ private SecStatusCode Save(string account, string service, string generic, int t
return secStatusCode;
}
- private SecRecord CreateRecord(string account, string service, string generic, int type, string value)
- {
- return new SecRecord(SecKind.GenericPassword)
- {
- Account = account,
- Service = service,
- Generic = generic,
- CreatorType = type,
- ValueData = NSData.FromString(value, NSStringEncoding.UTF8),
- AccessGroup = keychainGroup,
- Accessible = _defaultAccessiblityPolicy,
- Synchronizable = _defaultSyncSetting,
- };
- }
- private SecStatusCode Remove(string account, string service, int type)
+ private SecStatusCode Remove(IiOSKey key)
{
var record = new SecRecord(SecKind.GenericPassword)
{
- Account = account,
- Service = service,
- CreatorType = type,
- AccessGroup = keychainGroup
+ Account = key.iOSAccount,
+ Service = key.iOSService,
+ CreatorType = key.iOSType,
+ AccessGroup = _keychainGroup
};
return SecKeyChain.Remove(record);
@@ -338,7 +273,7 @@ private SecStatusCode Update(SecRecord updatedRecord)
Account = updatedRecord.Account,
Service = updatedRecord.Service,
CreatorType = updatedRecord.CreatorType,
- AccessGroup = keychainGroup
+ AccessGroup = _keychainGroup
};
var attributesToUpdate = new SecRecord()
{
@@ -348,88 +283,73 @@ private SecStatusCode Update(SecRecord updatedRecord)
return SecKeyChain.Update(currentRecord, attributesToUpdate);
}
- private void RemoveAll(int type)
+ private void RemoveByType(int type)
{
var queryRecord = new SecRecord(SecKind.GenericPassword)
{
CreatorType = type,
- AccessGroup = keychainGroup
+ AccessGroup = _keychainGroup
};
SecKeyChain.Remove(queryRecord);
}
public void Clear()
{
- RemoveAll((int)CredentialAttrType.AccessToken);
- RemoveAll((int)CredentialAttrType.RefreshToken);
- RemoveAll((int)CredentialAttrType.IdToken);
+ RemoveByType((int)MsalCacheKeys.iOSCredentialAttrType.AccessToken);
+ RemoveByType((int)MsalCacheKeys.iOSCredentialAttrType.RefreshToken);
+ RemoveByType((int)MsalCacheKeys.iOSCredentialAttrType.IdToken);
- RemoveAll(AuthorityTypeToAttrType[AuthorityType.MSSTS.ToString()]);
+ RemoveByType(MsalCacheKeys.iOSAuthorityTypeToAttrType[AuthorityType.MSSTS.ToString()]);
}
public MsalAccessTokenCacheItem GetAccessToken(MsalAccessTokenCacheKey accessTokenKey)
{
- var account = accessTokenKey.GetiOSAccountKey();
- var service = accessTokenKey.GetiOSServiceKey();
-
- var type = (int)CredentialAttrType.AccessToken;
-
- return MsalAccessTokenCacheItem.FromJsonString(GetValue(account, service, type));
+ return MsalAccessTokenCacheItem.FromJsonString(GetPayload(accessTokenKey));
}
public MsalRefreshTokenCacheItem GetRefreshToken(MsalRefreshTokenCacheKey refreshTokenKey)
{
- var account = refreshTokenKey.GetiOSAccountKey();
- var service = refreshTokenKey.GetiOSServiceKey();
-
-
- var type = (int)CredentialAttrType.RefreshToken;
-
- return MsalRefreshTokenCacheItem.FromJsonString(GetValue(account, service, type));
+ return MsalRefreshTokenCacheItem.FromJsonString(GetPayload(refreshTokenKey));
}
public MsalIdTokenCacheItem GetIdToken(MsalIdTokenCacheKey idTokenKey)
{
- var account = idTokenKey.GetiOSAccountKey();
- var service = idTokenKey.GetiOSServiceKey();
+ return MsalIdTokenCacheItem.FromJsonString(GetPayload(idTokenKey));
- var type = (int)CredentialAttrType.IdToken;
-
- return MsalIdTokenCacheItem.FromJsonString(GetValue(account, service, type));
}
public MsalAccountCacheItem GetAccount(MsalAccountCacheKey accountKey)
{
- var account = accountKey.GetiOSAccountKey();
- var service = accountKey.GetiOSServiceKey();
-
- var type = AuthorityTypeToAttrType[AuthorityType.MSSTS.ToString()];
-
- return MsalAccountCacheItem.FromJsonString(GetValue(account, service, type));
+ return MsalAccountCacheItem.FromJsonString(GetPayload(accountKey));
}
- ///
- public int RefreshTokenCount => throw new NotImplementedException();
-
- ///
- public int AccessTokenCount => throw new NotImplementedException();
+ #region AppMetatada - not implemented on iOS
+ public MsalAppMetadataCacheItem ReadAppMetadata(MsalAppMetadataCacheKey appMetadataKey)
+ {
+ //return MsalAppMetadataCacheItem.FromJsonString(GetPayload(appMetadataKey));
+ throw new NotImplementedException();
+ }
- ///
- public int AccountCount => throw new NotImplementedException();
+ public void WriteAppMetadata(MsalAppMetadataCacheItem appMetadata)
+ {
+ //Save(appMetadata.GetKey(), appMetadata.ToJsonString());
+ throw new NotImplementedException();
+ }
- ///
- public int IdTokenCount => throw new NotImplementedException();
+ public void SaveAppMetadata(MsalAppMetadataCacheItem item)
+ {
+ throw new NotImplementedException();
+ }
- ///
- public void ClearRefreshTokens()
+ public IEnumerable GetAllAppMetadata()
{
throw new NotImplementedException();
}
- ///
- public void ClearAccessTokens()
+ public MsalAppMetadataCacheItem GetAppMetadata(MsalAppMetadataCacheKey appMetadataKey)
{
throw new NotImplementedException();
}
+ #endregion
}
}
diff --git a/src/Microsoft.Identity.Client/Platforms/net45/NetDesktopFeatureFlags.cs b/src/Microsoft.Identity.Client/Platforms/net45/NetDesktopFeatureFlags.cs
new file mode 100644
index 0000000000..ff08e0b8e7
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Platforms/net45/NetDesktopFeatureFlags.cs
@@ -0,0 +1,36 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
+
+namespace Microsoft.Identity.Client.Platforms.net45
+{
+ internal class NetDesktopFeatureFlags : IFeatureFlags
+ {
+ public bool IsFociEnabled => true;
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/net45/NetDesktopPlatformProxy.cs b/src/Microsoft.Identity.Client/Platforms/net45/NetDesktopPlatformProxy.cs
index 130b438f92..6ac08f58bd 100644
--- a/src/Microsoft.Identity.Client/Platforms/net45/NetDesktopPlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/Platforms/net45/NetDesktopPlatformProxy.cs
@@ -232,15 +232,11 @@ protected override string InternalGetProductName()
}
///
- protected override ICryptographyManager InternalGetCryptographyManager()
- {
- return new NetDesktopCryptographyManager();
- }
+ protected override ICryptographyManager InternalGetCryptographyManager() => new NetDesktopCryptographyManager();
///
- protected override IPlatformLogger InternalGetPlatformLogger()
- {
- return new EventSourcePlatformLogger();
- }
+ protected override IPlatformLogger InternalGetPlatformLogger() => new EventSourcePlatformLogger();
+
+ protected override IFeatureFlags CreateFeatureFlags() => new NetDesktopFeatureFlags();
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/netcore/NetCoreFeatureFlags.cs b/src/Microsoft.Identity.Client/Platforms/netcore/NetCoreFeatureFlags.cs
new file mode 100644
index 0000000000..50a33dfb32
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Platforms/netcore/NetCoreFeatureFlags.cs
@@ -0,0 +1,36 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
+
+namespace Microsoft.Identity.Client.Platforms.netcore
+{
+ internal class NetCoreFeatureFlags : IFeatureFlags
+ {
+ public bool IsFociEnabled => true;
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/netcore/NetCorePlatformProxy.cs b/src/Microsoft.Identity.Client/Platforms/netcore/NetCorePlatformProxy.cs
index 2315bad0be..1ef8416fb7 100644
--- a/src/Microsoft.Identity.Client/Platforms/netcore/NetCorePlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/Platforms/netcore/NetCorePlatformProxy.cs
@@ -147,5 +147,7 @@ public override ITokenCacheAccessor CreateTokenCacheAccessor()
protected override IWebUIFactory CreateWebUiFactory() => new WebUIFactory();
protected override ICryptographyManager InternalGetCryptographyManager() => new NetCoreCryptographyManager();
protected override IPlatformLogger InternalGetPlatformLogger() => new EventSourcePlatformLogger();
+
+ protected override IFeatureFlags CreateFeatureFlags() => new NetCoreFeatureFlags();
}
}
diff --git a/src/Microsoft.Identity.Client/Platforms/netstandard13/NetDesktopFeatureFlags.cs b/src/Microsoft.Identity.Client/Platforms/netstandard13/NetDesktopFeatureFlags.cs
new file mode 100644
index 0000000000..bd9a3acc7f
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Platforms/netstandard13/NetDesktopFeatureFlags.cs
@@ -0,0 +1,36 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
+
+namespace Microsoft.Identity.Client.Platforms.Android
+{
+ internal class NetDesktopFeatureFlags : IFeatureFlags
+ {
+ public bool IsFociEnabled => true;
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/netstandard13/NetStandard13PlatformProxy.cs b/src/Microsoft.Identity.Client/Platforms/netstandard13/NetStandard13PlatformProxy.cs
index 5296e8d337..028c205a73 100644
--- a/src/Microsoft.Identity.Client/Platforms/netstandard13/NetStandard13PlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/Platforms/netstandard13/NetStandard13PlatformProxy.cs
@@ -146,5 +146,7 @@ public override ITokenCacheAccessor CreateTokenCacheAccessor()
protected override IWebUIFactory CreateWebUiFactory() => new WebUIFactory();
protected override ICryptographyManager InternalGetCryptographyManager() => new NetStandard13CryptographyManager();
protected override IPlatformLogger InternalGetPlatformLogger() => new EventSourcePlatformLogger();
+
+ protected override IFeatureFlags CreateFeatureFlags() => new NetStandardFeatureFlags();
}
}
diff --git a/src/Microsoft.Identity.Client/Platforms/netstandard13/NetStandardFeatureFlags.cs b/src/Microsoft.Identity.Client/Platforms/netstandard13/NetStandardFeatureFlags.cs
new file mode 100644
index 0000000000..770027af59
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Platforms/netstandard13/NetStandardFeatureFlags.cs
@@ -0,0 +1,39 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
+
+namespace Microsoft.Identity.Client.Platforms.netstandard13
+{
+ ///
+ /// These control the behaviour of platforms targetting directly NetStandard (e.g. WinRT)
+ ///
+ internal class NetStandardFeatureFlags : IFeatureFlags
+ {
+ public bool IsFociEnabled => true;
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/uap/UapFeatureFlags.cs b/src/Microsoft.Identity.Client/Platforms/uap/UapFeatureFlags.cs
new file mode 100644
index 0000000000..9beafae50c
--- /dev/null
+++ b/src/Microsoft.Identity.Client/Platforms/uap/UapFeatureFlags.cs
@@ -0,0 +1,36 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
+
+namespace Microsoft.Identity.Client.Platforms.uap
+{
+ internal class UapFeatureFlags : IFeatureFlags
+ {
+ public bool IsFociEnabled => true;
+ }
+}
diff --git a/src/Microsoft.Identity.Client/Platforms/uap/UapPlatformProxy.cs b/src/Microsoft.Identity.Client/Platforms/uap/UapPlatformProxy.cs
index 7eb8260945..8451a82dde 100644
--- a/src/Microsoft.Identity.Client/Platforms/uap/UapPlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/Platforms/uap/UapPlatformProxy.cs
@@ -201,24 +201,16 @@ protected override string InternalGetDeviceId()
return new EasClientDeviceInformation()?.Id.ToString();
}
- public override ILegacyCachePersistence CreateLegacyCachePersistence()
- {
- return new UapLegacyCachePersistence(Logger, CryptographyManager);
- }
+ public override ILegacyCachePersistence CreateLegacyCachePersistence() => new UapLegacyCachePersistence(Logger, CryptographyManager);
- public override ITokenCacheAccessor CreateTokenCacheAccessor()
- {
- return new InMemoryTokenCacheAccessor();
- }
+ public override ITokenCacheAccessor CreateTokenCacheAccessor() => new InMemoryTokenCacheAccessor();
- public override ITokenCacheBlobStorage CreateTokenCacheBlobStorage()
- {
- return new UapTokenCacheBlobStorage(CryptographyManager, Logger);
- }
+ public override ITokenCacheBlobStorage CreateTokenCacheBlobStorage() => new UapTokenCacheBlobStorage(CryptographyManager, Logger);
protected override IWebUIFactory CreateWebUiFactory() => new WebUIFactory();
protected override ICryptographyManager InternalGetCryptographyManager() => new UapCryptographyManager();
protected override IPlatformLogger InternalGetPlatformLogger() => new EventSourcePlatformLogger();
+ protected override IFeatureFlags CreateFeatureFlags() => new UapFeatureFlags();
}
}
diff --git a/src/Microsoft.Identity.Client/PlatformsCommon/Interfaces/IFeatureFlags.cs b/src/Microsoft.Identity.Client/PlatformsCommon/Interfaces/IFeatureFlags.cs
new file mode 100644
index 0000000000..099feab333
--- /dev/null
+++ b/src/Microsoft.Identity.Client/PlatformsCommon/Interfaces/IFeatureFlags.cs
@@ -0,0 +1,34 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// ------------------------------------------------------------------------------
+
+namespace Microsoft.Identity.Client.PlatformsCommon.Interfaces
+{
+ internal interface IFeatureFlags
+ {
+ bool IsFociEnabled { get; }
+ }
+}
diff --git a/src/Microsoft.Identity.Client/PlatformsCommon/Interfaces/IPlatformProxy.cs b/src/Microsoft.Identity.Client/PlatformsCommon/Interfaces/IPlatformProxy.cs
index 5c9ae483a3..ee9153c307 100644
--- a/src/Microsoft.Identity.Client/PlatformsCommon/Interfaces/IPlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/PlatformsCommon/Interfaces/IPlatformProxy.cs
@@ -105,6 +105,10 @@ internal interface IPlatformProxy
IPlatformLogger PlatformLogger { get; }
IWebUIFactory GetWebUiFactory();
- void SetWebUiFactory(IWebUIFactory webUiFactory);
+ void /* for test */ SetWebUiFactory(IWebUIFactory webUiFactory);
+
+ IFeatureFlags GetFeatureFlags();
+
+ void /* for test */ SetFeatureFlags(IFeatureFlags featureFlags);
}
}
diff --git a/src/Microsoft.Identity.Client/PlatformsCommon/Shared/AbstractPlatformProxy.cs b/src/Microsoft.Identity.Client/PlatformsCommon/Shared/AbstractPlatformProxy.cs
index 3645d2ce4c..e8b13841f3 100644
--- a/src/Microsoft.Identity.Client/PlatformsCommon/Shared/AbstractPlatformProxy.cs
+++ b/src/Microsoft.Identity.Client/PlatformsCommon/Shared/AbstractPlatformProxy.cs
@@ -61,6 +61,8 @@ protected AbstractPlatformProxy(ICoreLogger logger)
}
protected IWebUIFactory OverloadWebUiFactory { get; set; }
+ protected IFeatureFlags OverloadFeatureFlags { get; set; }
+
protected ICoreLogger Logger { get; }
///
@@ -148,6 +150,7 @@ public string GetProductName()
public IPlatformLogger PlatformLogger => _platformLogger.Value;
protected abstract IWebUIFactory CreateWebUiFactory();
+ protected abstract IFeatureFlags CreateFeatureFlags();
protected abstract string InternalGetDeviceModel();
protected abstract string InternalGetOperatingSystem();
protected abstract string InternalGetProcessorArchitecture();
@@ -162,5 +165,15 @@ public virtual ITokenCacheBlobStorage CreateTokenCacheBlobStorage()
{
return new NullTokenCacheBlobStorage();
}
+
+ public virtual IFeatureFlags GetFeatureFlags()
+ {
+ return OverloadFeatureFlags ?? CreateFeatureFlags();
+ }
+
+ public void SetFeatureFlags(IFeatureFlags featureFlags)
+ {
+ OverloadFeatureFlags = featureFlags;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryTokenCacheAccessor.cs b/src/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryTokenCacheAccessor.cs
index 8513e5f713..27d74f4ef7 100644
--- a/src/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryTokenCacheAccessor.cs
+++ b/src/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryTokenCacheAccessor.cs
@@ -41,65 +41,49 @@ namespace Microsoft.Identity.Client.PlatformsCommon.Shared
///
internal class InMemoryTokenCacheAccessor : ITokenCacheAccessor
{
- internal readonly IDictionary AccessTokenCacheDictionary =
- new ConcurrentDictionary();
+ private readonly IDictionary _accessTokenCacheDictionary =
+ new Dictionary();
- internal readonly IDictionary RefreshTokenCacheDictionary =
- new ConcurrentDictionary();
+ private readonly IDictionary _refreshTokenCacheDictionary =
+ new Dictionary();
- internal readonly IDictionary IdTokenCacheDictionary =
- new ConcurrentDictionary();
+ private readonly IDictionary _idTokenCacheDictionary =
+ new Dictionary();
- internal readonly IDictionary AccountCacheDictionary =
- new ConcurrentDictionary();
+ private readonly IDictionary _accountCacheDictionary =
+ new Dictionary();
- ///
- public int RefreshTokenCount => RefreshTokenCacheDictionary.Count;
-
- ///
- public int AccessTokenCount => AccessTokenCacheDictionary.Count;
-
- ///
- public int AccountCount => AccountCacheDictionary.Count;
-
- ///
- public int IdTokenCount => IdTokenCacheDictionary.Count;
-
- ///
- public void ClearRefreshTokens()
- {
- RefreshTokenCacheDictionary.Clear();
- }
-
- ///
- public void ClearAccessTokens()
- {
- AccessTokenCacheDictionary.Clear();
- }
+ private readonly IDictionary _appMetadataDictionary =
+ new Dictionary();
public void SaveAccessToken(MsalAccessTokenCacheItem item)
{
- AccessTokenCacheDictionary[item.GetKey().ToString()] = item;
+ _accessTokenCacheDictionary[item.GetKey().ToString()] = item;
}
public void SaveRefreshToken(MsalRefreshTokenCacheItem item)
{
- RefreshTokenCacheDictionary[item.GetKey().ToString()] = item;
+ _refreshTokenCacheDictionary[item.GetKey().ToString()] = item;
}
public void SaveIdToken(MsalIdTokenCacheItem item)
{
- IdTokenCacheDictionary[item.GetKey().ToString()] = item;
+ _idTokenCacheDictionary[item.GetKey().ToString()] = item;
}
public void SaveAccount(MsalAccountCacheItem item)
{
- AccountCacheDictionary[item.GetKey().ToString()] = item;
+ _accountCacheDictionary[item.GetKey().ToString()] = item;
+ }
+
+ public void SaveAppMetadata(MsalAppMetadataCacheItem item)
+ {
+ _appMetadataDictionary[item.GetKey().ToString()] = item;
}
public MsalAccessTokenCacheItem GetAccessToken(MsalAccessTokenCacheKey accessTokenKey)
{
- if (AccessTokenCacheDictionary.TryGetValue(accessTokenKey.ToString(), out var cacheItem))
+ if (_accessTokenCacheDictionary.TryGetValue(accessTokenKey.ToString(), out var cacheItem))
{
return cacheItem;
}
@@ -109,7 +93,7 @@ public MsalAccessTokenCacheItem GetAccessToken(MsalAccessTokenCacheKey accessTok
public MsalRefreshTokenCacheItem GetRefreshToken(MsalRefreshTokenCacheKey refreshTokenKey)
{
- if (RefreshTokenCacheDictionary.TryGetValue(refreshTokenKey.ToString(), out var cacheItem))
+ if (_refreshTokenCacheDictionary.TryGetValue(refreshTokenKey.ToString(), out var cacheItem))
{
return cacheItem;
}
@@ -119,7 +103,7 @@ public MsalRefreshTokenCacheItem GetRefreshToken(MsalRefreshTokenCacheKey refres
public MsalIdTokenCacheItem GetIdToken(MsalIdTokenCacheKey idTokenKey)
{
- if (IdTokenCacheDictionary.TryGetValue(idTokenKey.ToString(), out var cacheItem))
+ if (_idTokenCacheDictionary.TryGetValue(idTokenKey.ToString(), out var cacheItem))
{
return cacheItem;
}
@@ -128,7 +112,7 @@ public MsalIdTokenCacheItem GetIdToken(MsalIdTokenCacheKey idTokenKey)
public MsalAccountCacheItem GetAccount(MsalAccountCacheKey accountKey)
{
- if (AccountCacheDictionary.TryGetValue(accountKey.ToString(), out var cacheItem))
+ if (_accountCacheDictionary.TryGetValue(accountKey.ToString(), out var cacheItem))
{
return cacheItem;
}
@@ -138,42 +122,53 @@ public MsalAccountCacheItem GetAccount(MsalAccountCacheKey accountKey)
public void DeleteAccessToken(MsalAccessTokenCacheKey cacheKey)
{
- AccessTokenCacheDictionary.Remove(cacheKey.ToString());
+ _accessTokenCacheDictionary.Remove(cacheKey.ToString());
}
public void DeleteRefreshToken(MsalRefreshTokenCacheKey cacheKey)
{
- RefreshTokenCacheDictionary.Remove(cacheKey.ToString());
+ _refreshTokenCacheDictionary.Remove(cacheKey.ToString());
}
public void DeleteIdToken(MsalIdTokenCacheKey cacheKey)
{
- IdTokenCacheDictionary.Remove(cacheKey.ToString());
+ _idTokenCacheDictionary.Remove(cacheKey.ToString());
}
public void DeleteAccount(MsalAccountCacheKey cacheKey)
{
- AccountCacheDictionary.Remove(cacheKey.ToString());
+ _accountCacheDictionary.Remove(cacheKey.ToString());
}
- public ICollection GetAllAccessTokens()
+ public IEnumerable GetAllAccessTokens()
{
- return new ReadOnlyCollection(AccessTokenCacheDictionary.Values.ToList());
+ return new ReadOnlyCollection(
+ _accessTokenCacheDictionary.Values.ToList());
}
- public ICollection GetAllRefreshTokens()
+ public IEnumerable GetAllRefreshTokens()
{
- return new ReadOnlyCollection(RefreshTokenCacheDictionary.Values.ToList());
+ return new ReadOnlyCollection(
+ _refreshTokenCacheDictionary.Values.ToList());
+
+ }
+
+ public IEnumerable GetAllIdTokens()
+ {
+ return new ReadOnlyCollection(
+ _idTokenCacheDictionary.Values.ToList());
}
- public ICollection GetAllIdTokens()
+ public IEnumerable GetAllAccounts()
{
- return new ReadOnlyCollection(IdTokenCacheDictionary.Values.ToList());
+ return new ReadOnlyCollection(
+ _accountCacheDictionary.Values.ToList());
}
- public ICollection GetAllAccounts()
+ public IEnumerable GetAllAppMetadata()
{
- return new ReadOnlyCollection(AccountCacheDictionary.Values.ToList());
+ return new ReadOnlyCollection(
+ _appMetadataDictionary.Values.ToList());
}
public void SetiOSKeychainSecurityGroup(string keychainSecurityGroup)
@@ -183,10 +178,20 @@ public void SetiOSKeychainSecurityGroup(string keychainSecurityGroup)
public void Clear()
{
- AccessTokenCacheDictionary.Clear();
- RefreshTokenCacheDictionary.Clear();
- IdTokenCacheDictionary.Clear();
- AccountCacheDictionary.Clear();
+ _accessTokenCacheDictionary.Clear();
+ _refreshTokenCacheDictionary.Clear();
+ _idTokenCacheDictionary.Clear();
+ _accountCacheDictionary.Clear();
+ // app metadata isn't removable
+ }
+
+ public MsalAppMetadataCacheItem GetAppMetadata(MsalAppMetadataCacheKey appMetadataKey)
+ {
+ if (_appMetadataDictionary.TryGetValue(appMetadataKey.ToString(), out var cacheItem))
+ {
+ return cacheItem;
+ }
+ return null;
}
}
}
diff --git a/src/Microsoft.Identity.Client/TokenCache.cs b/src/Microsoft.Identity.Client/TokenCache.cs
index 64d7c2945a..ccfcec5099 100644
--- a/src/Microsoft.Identity.Client/TokenCache.cs
+++ b/src/Microsoft.Identity.Client/TokenCache.cs
@@ -63,6 +63,8 @@ public sealed class TokenCache : ITokenCacheInternal
private ICoreLogger _logger => ServiceBundle.DefaultLogger;
private readonly ITokenCacheBlobStorage _defaultTokenCacheBlobStorage;
+ private readonly IFeatureFlags _featureFlags;
+
private TokenCacheCallback _userConfiguredBeforeAccess;
private TokenCacheCallback _userConfiguredAfterAccess;
private TokenCacheCallback _userConfiguredBeforeWrite;
@@ -83,18 +85,19 @@ public sealed class TokenCache : ITokenCacheInternal
/// The recommended way to get a cache is by using
/// and IConfidentialClientApplication.AppTokenCache once the app is created.
///
- public TokenCache()
+ public TokenCache() : this((IServiceBundle)null)
{
- ServiceBundle = null;
- var proxy = PlatformProxyFactory.CreatePlatformProxy(null);
- _accessor = proxy.CreateTokenCacheAccessor();
- _defaultTokenCacheBlobStorage = proxy.CreateTokenCacheBlobStorage();
- LegacyCachePersistence = proxy.CreateLegacyCachePersistence();
}
- internal TokenCache(IServiceBundle serviceBundle) : this()
+ internal TokenCache(IServiceBundle serviceBundle)
{
SetServiceBundle(serviceBundle);
+
+ var proxy = serviceBundle?.PlatformProxy ?? PlatformProxyFactory.CreatePlatformProxy(null);
+ _accessor = proxy.CreateTokenCacheAccessor();
+ _featureFlags = proxy.GetFeatureFlags();
+ _defaultTokenCacheBlobStorage = proxy.CreateTokenCacheBlobStorage();
+ LegacyCachePersistence = proxy.CreateLegacyCachePersistence();
}
internal void SetServiceBundle(IServiceBundle serviceBundle)
@@ -111,11 +114,11 @@ internal void SetServiceBundle(IServiceBundle serviceBundle)
///
///
internal TokenCache(IServiceBundle serviceBundle, ILegacyCachePersistence legacyCachePersistenceForTest)
+ : this(serviceBundle)
{
- ServiceBundle = serviceBundle;
- _accessor = ServiceBundle.PlatformProxy.CreateTokenCacheAccessor();
+ SetServiceBundle(serviceBundle);
+
LegacyCachePersistence = legacyCachePersistenceForTest;
- _defaultTokenCacheBlobStorage = ServiceBundle.PlatformProxy.CreateTokenCacheBlobStorage();
}
///
@@ -134,6 +137,7 @@ internal TokenCache(IServiceBundle serviceBundle, ILegacyCachePersistence legacy
internal string ClientId => ServiceBundle.Config.ClientId;
+ #region Notifications
///
/// Notification method called before any library method accesses the cache.
///
@@ -202,7 +206,9 @@ internal void OnBeforeWrite(TokenCacheNotificationArgs args)
BeforeWrite?.Invoke(args);
}
- Tuple ITokenCacheInternal.SaveAccessAndRefreshToken(
+ #endregion
+
+ Tuple ITokenCacheInternal.SaveTokenResponse(
AuthenticationRequestParameters requestParams,
MsalTokenResponse response)
{
@@ -268,35 +274,40 @@ Tuple ITokenCacheInternal.SaveAc
_accessor.SaveAccessToken(msalAccessTokenCacheItem);
- if (idToken != null)
- {
- _accessor.SaveIdToken(msalIdTokenCacheItem);
-
- var msalAccountCacheItem = new MsalAccountCacheItem(preferredEnvironmentHost, response, preferredUsername, tenantId);
-
- _accessor.SaveAccount(msalAccountCacheItem);
- }
+ if (idToken != null)
+ {
+ _accessor.SaveIdToken(msalIdTokenCacheItem);
+ var msalAccountCacheItem = new MsalAccountCacheItem(preferredEnvironmentHost, response, preferredUsername, tenantId);
+ _accessor.SaveAccount(msalAccountCacheItem);
+ }
- // if server returns the refresh token back, save it in the cache.
- if (response.RefreshToken != null)
+ // if server returns the refresh token back, save it in the cache.
+ if (response.RefreshToken != null)
+ {
+ msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem(preferredEnvironmentHost, requestParams.ClientId, response);
+ if (!_featureFlags.IsFociEnabled)
{
- msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem(preferredEnvironmentHost, requestParams.ClientId, response);
- requestParams.RequestContext.Logger.Info("Saving RT in cache...");
- _accessor.SaveRefreshToken(msalRefreshTokenCacheItem);
+ msalRefreshTokenCacheItem.FamilyId = null;
}
- // save RT in ADAL cache for public clients
- // do not save RT in ADAL cache for MSAL B2C scenarios
- if (!requestParams.IsClientCredentialRequest && !requestParams.AuthorityInfo.AuthorityType.Equals(AppConfig.AuthorityType.B2C))
- {
- CacheFallbackOperations.WriteAdalRefreshToken(
- _logger,
- LegacyCachePersistence,
- msalRefreshTokenCacheItem,
- msalIdTokenCacheItem,
- Authority.CreateAuthorityUriWithHost(requestParams.TenantUpdatedCanonicalAuthority, preferredEnvironmentHost),
- msalIdTokenCacheItem.IdToken.ObjectId, response.Scope);
- }
+ requestParams.RequestContext.Logger.Info("Saving RT in cache...");
+ _accessor.SaveRefreshToken(msalRefreshTokenCacheItem);
+ }
+
+ UpdateAppMetadata(requestParams.ClientId, preferredEnvironmentHost, response.FamilyId);
+
+ // save RT in ADAL cache for public clients
+ // do not save RT in ADAL cache for MSAL B2C scenarios
+ if (!requestParams.IsClientCredentialRequest && !requestParams.AuthorityInfo.AuthorityType.Equals(AppConfig.AuthorityType.B2C))
+ {
+ CacheFallbackOperations.WriteAdalRefreshToken(
+ _logger,
+ LegacyCachePersistence,
+ msalRefreshTokenCacheItem,
+ msalIdTokenCacheItem,
+ Authority.CreateAuthorityUriWithHost(requestParams.TenantUpdatedCanonicalAuthority, preferredEnvironmentHost),
+ msalIdTokenCacheItem.IdToken.ObjectId, response.Scope);
+ }
}
finally
@@ -315,6 +326,15 @@ Tuple ITokenCacheInternal.SaveAc
}
}
+ private void UpdateAppMetadata(string clientId, string environment, string familyId)
+ {
+ if (_featureFlags.IsFociEnabled)
+ {
+ var metadataCacheItem = new MsalAppMetadataCacheItem(clientId, environment, familyId);
+ _accessor.SaveAppMetadata(metadataCacheItem);
+ }
+ }
+
private void DeleteAccessTokensWithIntersectingScopes(AuthenticationRequestParameters requestParams,
ISet environmentAliases, string tenantId, SortedSet scopeSet, string homeAccountId)
{
@@ -543,7 +563,9 @@ private string GetAccessTokenExpireLogMessageContent(MsalAccessTokenCacheItem ms
msalAccessTokenCacheItem.ExtendedExpiresOn);
}
- async Task ITokenCacheInternal.FindRefreshTokenAsync(AuthenticationRequestParameters requestParams)
+ async Task ITokenCacheInternal.FindRefreshTokenAsync(
+ AuthenticationRequestParameters requestParams,
+ string familyId)
{
using (ServiceBundle.TelemetryManager.CreateTelemetryHelper(requestParams.RequestContext.TelemetryRequestId, requestParams.RequestContext.ClientId,
new CacheEvent(CacheEvent.TokenCacheLookup) { TokenType = CacheEvent.TokenTypes.RT }))
@@ -573,30 +595,24 @@ async Task ITokenCacheInternal.FindRefreshTokenAsync(
Account = requestParams.Account
};
- MsalRefreshTokenCacheKey key = new MsalRefreshTokenCacheKey(
- preferredEnvironmentHost, requestParams.ClientId, requestParams.Account?.HomeAccountId?.Identifier);
+ // make sure to check preferredEnvironmentHost first
+ var allEnvAliases = new List() { preferredEnvironmentHost };
+ allEnvAliases.AddRange(environmentAliases);
+
+ var keysAcrossEnvs = allEnvAliases.Select(ea => new MsalRefreshTokenCacheKey(
+ ea,
+ requestParams.ClientId,
+ requestParams.Account?.HomeAccountId?.Identifier,
+ familyId));
+
OnBeforeAccess(args);
try
{
- MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = _accessor.GetRefreshToken(key);
-
- // trying to find rt by authority aliases
- if (msalRefreshTokenCacheItem == null)
- {
- var refreshTokens = _accessor.GetAllRefreshTokens();
-
- foreach (var refreshToken in refreshTokens)
- {
- if (refreshToken.ClientId.Equals(requestParams.ClientId, StringComparison.OrdinalIgnoreCase) &&
- environmentAliases.Contains(refreshToken.Environment) &&
- requestParams.Account?.HomeAccountId?.Identifier == refreshToken.HomeAccountId)
- {
- msalRefreshTokenCacheItem = refreshToken;
- continue;
- }
- }
- }
+ // Try to load from all env aliases, but stop at the first valid one
+ MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = keysAcrossEnvs
+ .Select(key => _accessor.GetRefreshToken(key))
+ .FirstOrDefault(item => item != null);
requestParams.RequestContext.Logger.Info("Refresh token found in the cache? - " + (msalRefreshTokenCacheItem != null));
@@ -607,19 +623,22 @@ async Task ITokenCacheInternal.FindRefreshTokenAsync(
requestParams.RequestContext.Logger.Info("Checking ADAL cache for matching RT");
- if (requestParams.Account == null)
+ // ADAL legacy cache does not store FRTs
+ if (requestParams.Account != null && string.IsNullOrEmpty(familyId))
{
- return null;
+ return CacheFallbackOperations.GetAdalEntryForMsal(
+ _logger,
+ LegacyCachePersistence,
+ preferredEnvironmentHost,
+ environmentAliases,
+ requestParams.ClientId,
+ requestParams.LoginHint,
+ requestParams.Account.HomeAccountId?.Identifier,
+ null);
}
- return CacheFallbackOperations.GetAdalEntryForMsal(
- _logger,
- LegacyCachePersistence,
- preferredEnvironmentHost,
- environmentAliases,
- requestParams.ClientId,
- requestParams.LoginHint,
- requestParams.Account.HomeAccountId?.Identifier,
- null);
+
+ return null;
+
}
finally
{
@@ -629,6 +648,55 @@ async Task ITokenCacheInternal.FindRefreshTokenAsync(
}
}
+ async Task ITokenCacheInternal.IsFociMemberAsync(AuthenticationRequestParameters requestParams, string familyId)
+ {
+ var logger = requestParams.RequestContext.Logger;
+ if (requestParams?.AuthorityInfo?.CanonicalAuthority == null)
+ {
+ logger.Warning("No authority details, can't check app metadta. Returning unkown");
+ return null;
+ }
+
+ var instanceDiscoveryMetadataEntry = await GetCachedOrDiscoverAuthorityMetaDataAsync(
+ requestParams.AuthorityInfo.CanonicalAuthority,
+ requestParams.RequestContext).ConfigureAwait(false);
+
+ var environmentAliases = GetEnvironmentAliases(
+ requestParams.AuthorityInfo.CanonicalAuthority,
+ instanceDiscoveryMetadataEntry);
+
+ TokenCacheNotificationArgs args = new TokenCacheNotificationArgs
+ {
+ TokenCache = this,
+ ClientId = ClientId,
+ Account = requestParams?.Account,
+ HasStateChanged = false
+ };
+
+ //TODO: bogavril - is the env ok here? Can I cache it or pass it in?
+ MsalAppMetadataCacheItem appMetadata;
+ lock (LockObject)
+ {
+ OnBeforeAccess(args);
+
+ appMetadata =
+ environmentAliases
+ .Select(env => _accessor.GetAppMetadata(new MsalAppMetadataCacheKey(ClientId, env)))
+ .FirstOrDefault(item => item != null);
+
+ OnAfterAccess(args);
+ }
+
+ if (appMetadata == null)
+ {
+ logger.Warning("No app metadata found. Returning unkown");
+ return null;
+ }
+
+ return appMetadata.FamilyId == familyId;
+ }
+
+
///
public void SetIosKeychainSecurityGroup(string securityGroup)
{
@@ -745,7 +813,7 @@ IEnumerable ITokenCacheInternal.GetAccounts(string authority)
OnBeforeAccess(args);
try
{
- tokenCacheItems = ((ITokenCacheInternal)this).GetAllRefreshTokens(true);
+ tokenCacheItems = ((ITokenCacheInternal)this).GetAllRefreshTokens(false);
accountCacheItems = ((ITokenCacheInternal)this).GetAllAccounts();
adalUsersResult = CacheFallbackOperations.GetAllAdalUsersForMsal(_logger, LegacyCachePersistence, ClientId);
}
@@ -784,7 +852,7 @@ IEnumerable ITokenCacheInternal.GetAccounts(string authority)
}
var accounts = new List(clientInfoToAccountMap.Values);
- List uniqueUserNames = clientInfoToAccountMap.Values.Select(o => o.Username).Distinct().ToList();
+ var uniqueUserNames = clientInfoToAccountMap.Values.Select(o => o.Username).Distinct().ToList();
foreach (AdalUserInfo user in adalUsersWithoutClientInfo)
{
@@ -839,6 +907,8 @@ IEnumerable ITokenCacheInternal.GetAllAccounts()
}
}
+
+ #region Removal methods
void ITokenCacheInternal.RemoveAccount(IAccount account, RequestContext requestContext)
{
lock (LockObject)
@@ -884,7 +954,9 @@ void ITokenCacheInternal.RemoveMsalAccount(IAccount account, RequestContext requ
// adalv3 account
return;
}
- var allRefreshTokens = ((ITokenCacheInternal)this).GetAllRefreshTokens(true)
+
+ // Delete ALL refresh tokens associated with this account
+ var allRefreshTokens = ((ITokenCacheInternal)this).GetAllRefreshTokens(false)
.Where(item => item.HomeAccountId.Equals(account.HomeAccountId.Identifier, StringComparison.OrdinalIgnoreCase))
.ToList();
@@ -894,7 +966,7 @@ void ITokenCacheInternal.RemoveMsalAccount(IAccount account, RequestContext requ
}
requestContext.Logger.Info("Deleted refresh token count - " + allRefreshTokens.Count);
- IList allAccessTokens = ((ITokenCacheInternal)this).GetAllAccessTokens(true)
+ IList allAccessTokens = ((ITokenCacheInternal)this).GetAllAccessTokens(false)
.Where(item => item.HomeAccountId.Equals(account.HomeAccountId.Identifier, StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (MsalAccessTokenCacheItem accessTokenCacheItem in allAccessTokens)
@@ -904,7 +976,7 @@ void ITokenCacheInternal.RemoveMsalAccount(IAccount account, RequestContext requ
requestContext.Logger.Info("Deleted access token count - " + allAccessTokens.Count);
- var allIdTokens = ((ITokenCacheInternal)this).GetAllIdTokens(true)
+ var allIdTokens = ((ITokenCacheInternal)this).GetAllIdTokens(false)
.Where(item => item.HomeAccountId.Equals(account.HomeAccountId.Identifier, StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (MsalIdTokenCacheItem idTokenCacheItem in allIdTokens)
@@ -913,6 +985,13 @@ void ITokenCacheInternal.RemoveMsalAccount(IAccount account, RequestContext requ
}
requestContext.Logger.Info("Deleted Id token count - " + allIdTokens.Count);
+
+ ((ITokenCacheInternal)this).GetAllAccounts()
+ .Where(item => item.HomeAccountId.Equals(account.HomeAccountId.Identifier, StringComparison.OrdinalIgnoreCase) &&
+ item.PreferredUsername.Equals(account.Username, StringComparison.OrdinalIgnoreCase))
+ .ToList()
+ .ForEach(accItem => _accessor.DeleteAccount(accItem.GetKey()));
+
}
internal void RemoveAdalUser(IAccount account)
@@ -967,6 +1046,10 @@ void ITokenCacheInternal.ClearMsalCache()
_accessor.Clear();
}
+ #endregion
+
+ #region Serialization
+
private bool UserHasConfiguredBlobSerialization()
{
return _userConfiguredBeforeAccess != null ||
@@ -974,7 +1057,7 @@ private bool UserHasConfiguredBlobSerialization()
_userConfiguredBeforeWrite != null;
}
-#if !ANDROID_BUILDTIME && !iOS_BUILDTIME
+#if !ANDROID_BUILDTIME && !iOS_BUILDTIME
///
/// Sets a delegate to be notified before any library method accesses the cache. This gives an option to the
@@ -1242,7 +1325,9 @@ private static void GuardOnMobilePlatforms()
"For more details about custom token cache serialization, visit https://aka.ms/msal-net-serialization");
#endif
}
-#endif // !ANDROID_BUILDTIME && !iOS_BUILDTIME
+
+#endif // !ANDROID_BUILDTIME && !iOS_BUILDTIME
+ #endregion
}
}
diff --git a/tests/Microsoft.Identity.Test.Common/Core/Helpers/AssertException.cs b/tests/Microsoft.Identity.Test.Common/Core/Helpers/AssertException.cs
index a3cfc4fffe..c27858b403 100644
--- a/tests/Microsoft.Identity.Test.Common/Core/Helpers/AssertException.cs
+++ b/tests/Microsoft.Identity.Test.Common/Core/Helpers/AssertException.cs
@@ -1,4 +1,4 @@
-//----------------------------------------------------------------------
+//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
// All rights reserved.
@@ -115,6 +115,11 @@ public static T TaskThrows(Func testCode, bool allowDerived = false)
if (exception is AggregateException aggEx)
{
+ if (aggEx.InnerException.GetType() == typeof(AssertFailedException))
+ {
+ throw aggEx.InnerException;
+ }
+
var exceptionsMatching = aggEx.InnerExceptions.OfType().ToList();
if (!exceptionsMatching.Any())
diff --git a/tests/Microsoft.Identity.Test.Common/Core/Helpers/TokenCacheExtensions.cs b/tests/Microsoft.Identity.Test.Common/Core/Helpers/TokenCacheExtensions.cs
index 5e50cacd67..60655ad5a6 100644
--- a/tests/Microsoft.Identity.Test.Common/Core/Helpers/TokenCacheExtensions.cs
+++ b/tests/Microsoft.Identity.Test.Common/Core/Helpers/TokenCacheExtensions.cs
@@ -28,6 +28,7 @@
using System;
using System.Collections.Generic;
using Microsoft.Identity.Client;
+using Microsoft.Identity.Client.Cache;
using Microsoft.Identity.Client.Cache.Items;
using Microsoft.Identity.Client.Core;
@@ -107,5 +108,21 @@ public static MsalAccountCacheItem GetAccount(
return null;
}
+
+ public static void ClearAccessTokens(this ITokenCacheAccessor accessor)
+ {
+ foreach (var item in accessor.GetAllAccessTokens())
+ {
+ accessor.DeleteAccessToken(item.GetKey());
+ }
+ }
+
+ public static void ClearRefreshTokens(this ITokenCacheAccessor accessor)
+ {
+ foreach (var item in accessor.GetAllRefreshTokens())
+ {
+ accessor.DeleteRefreshToken(item.GetKey());
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs
index 6813776e00..c33dc090a5 100644
--- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs
+++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs
@@ -56,6 +56,15 @@ internal static class MockHelpers
":\"" + CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId) +
"\",\"id_token_expires_in\":\"3600\"}";
+ public static readonly string FociTokenResponse =
+ "{\"token_type\":\"Bearer\",\"expires_in\":\"3599\",\"scope\":" +
+ "\"r1/scope1 r1/scope2\",\"access_token\":\"some-access-token\"" +
+ ",\"foci\":\"1\"" +
+ ",\"refresh_token\":\"OAAsomethingencryptedQwgAA\",\"client_info\"" +
+ ":\"" + CreateClientInfo() + "\",\"id_token\"" +
+ ":\"" + CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId) +
+ "\",\"id_token_expires_in\":\"3600\"}";
+
public static string CreateClientInfo()
{
return CreateClientInfo(MsalTestConstants.Uid, MsalTestConstants.Utid);
@@ -125,9 +134,10 @@ public static HttpResponseMessage CreateSuccessTokenResponseMessage(string scope
scopes, idToken, clientInfo));
}
- public static HttpResponseMessage CreateSuccessTokenResponseMessage()
+ public static HttpResponseMessage CreateSuccessTokenResponseMessage(bool foci = false)
{
- return CreateSuccessResponseMessage(DefaultTokenResponse);
+ return CreateSuccessResponseMessage(
+ foci ? FociTokenResponse : DefaultTokenResponse);
}
public static HttpResponseMessage CreateInvalidGrantTokenResponseMessage()
@@ -200,16 +210,17 @@ public static HttpResponseMessage CreateSuccessfulClientCredentialTokenResponseM
"{\"token_type\":\"Bearer\",\"expires_in\":\"3599\",\"access_token\":\"" + token + "\"}");
}
- public static HttpResponseMessage CreateSuccessTokenResponseMessage(string uniqueId, string displayableId, string[] scope)
+ public static HttpResponseMessage CreateSuccessTokenResponseMessage(string uniqueId, string displayableId, string[] scope, bool foci = false)
{
string idToken = CreateIdToken(uniqueId, displayableId, MsalTestConstants.Utid);
HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
- HttpContent content =
- new StringContent("{\"token_type\":\"Bearer\",\"expires_in\":\"3599\",\"scope\":\"" +
+ string stringContent = "{\"token_type\":\"Bearer\",\"expires_in\":\"3599\",\"scope\":\"" +
scope.AsSingleString() +
"\",\"access_token\":\"some-access-token\",\"refresh_token\":\"OAAsomethingencryptedQwgAA\",\"id_token\":\"" +
idToken +
- "\",\"id_token_expires_in\":\"3600\",\"client_info\":\"" + CreateClientInfo() + "\"}");
+ (foci ? "\",\"foci\":\"1" : "") +
+ "\",\"id_token_expires_in\":\"3600\",\"client_info\":\"" + CreateClientInfo() + "\"}";
+ HttpContent content = new StringContent(stringContent);
responseMessage.Content = content;
return responseMessage;
}
diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManager.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManager.cs
index 21a8e6e2ef..70ad7f423f 100644
--- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManager.cs
+++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManager.cs
@@ -68,6 +68,11 @@ public void AddMockHandler(HttpMessageHandler handler)
protected override HttpClient GetHttpClient()
{
+ if (_httpMessageHandlerQueue.Count == 0)
+ {
+ Assert.Fail("The MockHttpManager's queue is empty. Cannot serve another response");
+ }
+
var messageHandler = _httpMessageHandlerQueue.Dequeue();
var httpClient = new HttpClient(messageHandler)
{
@@ -80,4 +85,4 @@ protected override HttpClient GetHttpClient()
return httpClient;
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs
index 0819fc8c60..abb8c6388d 100644
--- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs
+++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs
@@ -66,7 +66,8 @@ public static void AddSuccessTokenResponseMockHandlerForPost(
this MockHttpManager httpManager,
string authority,
IDictionary bodyParameters = null,
- IDictionary queryParameters = null)
+ IDictionary queryParameters = null,
+ bool foci = false)
{
httpManager.AddMockHandler(
new MockHttpMessageHandler()
@@ -75,7 +76,7 @@ public static void AddSuccessTokenResponseMockHandlerForPost(
ExpectedMethod = HttpMethod.Post,
ExpectedPostData = bodyParameters,
ExpectedQueryParams = queryParameters,
- ResponseMessage = MockHelpers.CreateSuccessTokenResponseMessage()
+ ResponseMessage = MockHelpers.CreateSuccessTokenResponseMessage(foci)
});
}
@@ -167,4 +168,4 @@ public static void AddFailingRequest(this MockHttpManager httpManager, Exception
});
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/TokenCacheHelper.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/TokenCacheHelper.cs
index f430a1e7a1..f4ef400843 100644
--- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/TokenCacheHelper.cs
+++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/TokenCacheHelper.cs
@@ -56,11 +56,12 @@ internal void PopulateCacheForClientCredential(ITokenCacheAccessor accessor)
internal void PopulateCache(
ITokenCacheAccessor accessor,
string uid = MsalTestConstants.Uid,
- string utid = MsalTestConstants.Utid)
+ string utid = MsalTestConstants.Utid,
+ string clientId = MsalTestConstants.ClientId)
{
MsalAccessTokenCacheItem atItem = new MsalAccessTokenCacheItem(
MsalTestConstants.ProductionPrefCacheEnvironment,
- MsalTestConstants.ClientId,
+ clientId,
MsalTestConstants.Scope.AsSingleString(),
utid,
"",
@@ -72,8 +73,8 @@ internal void PopulateCache(
accessor.SaveAccessToken(atItem);
var idTokenCacheItem = new MsalIdTokenCacheItem(
- MsalTestConstants.ProductionPrefCacheEnvironment,
- MsalTestConstants.ClientId,
+ MsalTestConstants.ProductionPrefCacheEnvironment,
+ clientId,
MockHelpers.CreateIdToken(MsalTestConstants.UniqueId + "more", MsalTestConstants.DisplayableId),
MockHelpers.CreateClientInfo(uid, utid),
utid);
@@ -94,7 +95,7 @@ internal void PopulateCache(
atItem = new MsalAccessTokenCacheItem(
MsalTestConstants.ProductionPrefCacheEnvironment,
- MsalTestConstants.ClientId,
+ clientId,
MsalTestConstants.ScopeForAnotherResource.AsSingleString(),
utid,
"",
@@ -105,7 +106,14 @@ internal void PopulateCache(
// add another access token
accessor.SaveAccessToken(atItem);
- AddRefreshTokenToCache(accessor,uid, utid, MsalTestConstants.Name);
+ AddRefreshTokenToCache(accessor,uid, utid, clientId);
+
+ var appMetadataItem = new MsalAppMetadataCacheItem(
+ clientId,
+ MsalTestConstants.ProductionPrefCacheEnvironment,
+ null);
+
+ accessor.SaveAppMetadata(appMetadataItem);
}
internal void PopulateCacheWithOneAccessToken(ITokenCacheAccessor accessor)
@@ -146,13 +154,17 @@ internal void PopulateCacheWithOneAccessToken(ITokenCacheAccessor accessor)
new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(ValidExtendedExpiresIn)),
MockHelpers.CreateClientInfo());
- AddRefreshTokenToCache(accessor, MsalTestConstants.Uid, MsalTestConstants.Utid, MsalTestConstants.Name);
+ AddRefreshTokenToCache(accessor, MsalTestConstants.Uid, MsalTestConstants.Utid);
}
- public static void AddRefreshTokenToCache(ITokenCacheAccessor accessor, string uid, string utid, string name)
+ public static void AddRefreshTokenToCache(
+ ITokenCacheAccessor accessor,
+ string uid,
+ string utid,
+ string clientId = MsalTestConstants.ClientId)
{
var rtItem = new MsalRefreshTokenCacheItem
- (MsalTestConstants.ProductionPrefCacheEnvironment, MsalTestConstants.ClientId, "someRT", MockHelpers.CreateClientInfo(uid, utid));
+ (MsalTestConstants.ProductionPrefCacheEnvironment, clientId, "someRT", MockHelpers.CreateClientInfo(uid, utid));
accessor.SaveRefreshToken(rtItem);
}
@@ -177,4 +189,4 @@ public static void AddAccountToCache(ITokenCacheAccessor accessor, string uid, s
accessor.SaveAccount(accountCacheItem);
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Microsoft.Identity.Test.Common/MsalTestConstants.cs b/tests/Microsoft.Identity.Test.Common/MsalTestConstants.cs
index db82a7d7a2..7e52bf3e4f 100644
--- a/tests/Microsoft.Identity.Test.Common/MsalTestConstants.cs
+++ b/tests/Microsoft.Identity.Test.Common/MsalTestConstants.cs
@@ -29,6 +29,9 @@
using System.Globalization;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Cache;
+using Microsoft.Identity.Client.OAuth2;
+using Microsoft.Identity.Client.Utils;
+using Microsoft.Identity.Test.Common.Core.Mocks;
namespace Microsoft.Identity.Test.Unit
{
@@ -67,7 +70,8 @@ internal static class MsalTestConstants
public const string B2CLoginAuthorityMoonCake = "https://sometenantid.b2clogin.cn/tfp/sometenantid/policy/";
public const string B2CLoginAuthorityBlackforest = "https://sometenantid.b2clogin.de/tfp/sometenantid/policy/";
public const string ClientId = "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3";
- public static readonly string ClientId_1 = "d3adb33f-c1de-ed1c-c1de-deadb33fc1d3";
+ public const string ClientId2 = "d3adb33f-c1de-ed1c-c1de-deadb33fc1d3";
+ public const string FamilyId = "1";
public const string UniqueId = "unique_id";
public const string IdentityProvider = "my-idp";
public const string Name = "First Last";
@@ -124,6 +128,21 @@ public static string CreateUserIdentifier(string uid, string utid)
return string.Format(CultureInfo.InvariantCulture, "{0}.{1}", uid, utid);
}
+ public static MsalTokenResponse CreateMsalTokenResponse()
+ {
+ return new MsalTokenResponse
+ {
+ IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
+ AccessToken = "access-token",
+ ClientInfo = MockHelpers.CreateClientInfo(),
+ ExpiresIn = 3599,
+ CorrelationId = "correlation-id",
+ RefreshToken = "refresh-token",
+ Scope = MsalTestConstants.Scope.AsSingleString(),
+ TokenType = "Bearer"
+ };
+ }
+
public static readonly Account User = new Account(UserIdentifier, DisplayableId, ProductionPrefNetworkEnvironment);
public static readonly string OnPremiseAuthority = "https://fs.contoso.com/adfs/";
@@ -149,4 +168,4 @@ public static string CreateUserIdentifier(string uid, string utid)
public static readonly ClientCredential CredentialWithSecret = new ClientCredential(ClientSecret);
#endif
}
-}
\ No newline at end of file
+}
diff --git a/tests/Microsoft.Identity.Test.SideBySide/UnifiedCacheTests.cs b/tests/Microsoft.Identity.Test.SideBySide/UnifiedCacheTests.cs
index 05374de015..32b3af93af 100644
--- a/tests/Microsoft.Identity.Test.SideBySide/UnifiedCacheTests.cs
+++ b/tests/Microsoft.Identity.Test.SideBySide/UnifiedCacheTests.cs
@@ -459,10 +459,10 @@ public async Task UnifiedCache_Adalv3ToAdalV4ToMsal2MigrationIntegrationTestAsyn
Assert.IsNotNull(account.Environment);
// validate that Adal writes only RT and Account cache entities in Msal format
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.AccessTokenCount);
- Assert.AreEqual(1, ((ITokenCacheInternal)msalCache).Accessor.RefreshTokenCount);
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.IdTokenCount);
- Assert.AreEqual(1, ((ITokenCacheInternal)msalCache).Accessor.AccountCount);
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, ((ITokenCacheInternal)msalCache).Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(1, ((ITokenCacheInternal)msalCache).Accessor.GetAllAccounts().Count());
// make sure that adal v4 RT is usable by Msal
msalAuthResult = await msalPublicClient.AcquireTokenSilentAsync(MsalScopes, account).ConfigureAwait(false);
@@ -528,10 +528,10 @@ private void AssertMsalCacheIsEmpty()
TokenCache = msalCache
});
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.AccessTokenCount);
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.RefreshTokenCount);
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.IdTokenCount);
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.AccountCount);
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllAccounts().Count());
}
private void AssertNoCredentialsInMsalCache()
@@ -541,9 +541,10 @@ private void AssertNoCredentialsInMsalCache()
TokenCache = msalCache
});
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.AccessTokenCount);
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.RefreshTokenCount);
- Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.IdTokenCount);
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(0, ((ITokenCacheInternal)msalCache).Accessor.GetAllAccounts().Count());
}
}
}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/ApplicationGrantIntegrationTest.cs b/tests/Microsoft.Identity.Test.Unit.net45/ApplicationGrantIntegrationTest.cs
index c6e50c458f..8f7c7669e9 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/ApplicationGrantIntegrationTest.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/ApplicationGrantIntegrationTest.cs
@@ -26,6 +26,7 @@
//------------------------------------------------------------------------------
using System;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.AppConfig;
@@ -68,19 +69,19 @@ public async Task ApplicationGrantIntegrationTestAsync()
Assert.IsNull(res.Account);
// make sure user cache is empty
- Assert.AreEqual(0, userCache.Accessor.AccessTokenCount);
- Assert.AreEqual(0, userCache.Accessor.RefreshTokenCount);
- Assert.AreEqual(0, userCache.Accessor.IdTokenCount);
- Assert.AreEqual(0, userCache.Accessor.AccountCount);
+ Assert.AreEqual(0, userCache.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, userCache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(0, userCache.Accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(0, userCache.Accessor.GetAllAccounts().Count());
// make sure nothing was written to legacy cache
Assert.IsNull(userCache.LegacyPersistence.LoadCache());
// make sure only AT entity was stored in the App msal cache
- Assert.AreEqual(1, appCache.Accessor.AccessTokenCount);
- Assert.AreEqual(0, appCache.Accessor.RefreshTokenCount);
- Assert.AreEqual(0, appCache.Accessor.IdTokenCount);
- Assert.AreEqual(0, appCache.Accessor.AccountCount);
+ Assert.AreEqual(1, userCache.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, appCache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(0, appCache.Accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(0, appCache.Accessor.GetAllAccounts().Count());
Assert.IsNull(appCache.LegacyPersistence.LoadCache());
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/AuthorityAliasesTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/AuthorityAliasesTests.cs
index 9765f82b2d..9fca44bbdc 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/AuthorityAliasesTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/AuthorityAliasesTests.cs
@@ -210,4 +210,4 @@ private void ValidateCacheEntitiesEnvironment(ITokenCacheInternal cache, string
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/CacheSerializationTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/CacheSerializationTests.cs
index dbee1050a2..5bfb485b7f 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/CacheSerializationTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/CacheSerializationTests.cs
@@ -25,6 +25,7 @@
//
// ------------------------------------------------------------------------------
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -41,6 +42,11 @@ namespace Microsoft.Identity.Test.Unit.CacheTests
[TestClass]
public class CacheSerializationTests
{
+ private static readonly IEnumerable s_appMetadataKeys = new[] {
+ StorageJsonKeys.ClientId ,
+ StorageJsonKeys.Environment,
+ StorageJsonKeys.FamilyId};
+
private MsalAccessTokenCacheItem CreateAccessTokenItem()
{
return new MsalAccessTokenCacheItem
@@ -115,30 +121,39 @@ private ITokenCacheAccessor CreateTokenCacheAccessor()
{
var item = CreateAccessTokenItem();
item.Environment = item.Environment + $"_{i}"; // ensure we get unique cache keys
- accessor.AccessTokenCacheDictionary[item.GetKey().ToString()] = item;
+ accessor.SaveAccessToken(item);
}
for (int i = 1; i <= NumRefreshTokens; i++)
{
var item = CreateRefreshTokenItem();
item.Environment = item.Environment + $"_{i}"; // ensure we get unique cache keys
- accessor.RefreshTokenCacheDictionary[item.GetKey().ToString()] = item;
+ accessor.SaveRefreshToken(item);
}
+ // Create an FRT
+ var frt = CreateRefreshTokenItem();
+ frt.FamilyId = "1";
+ accessor.SaveRefreshToken(frt);
+
for (int i = 1; i <= NumIdTokens; i++)
{
var item = CreateIdTokenItem();
item.Environment = item.Environment + $"_{i}"; // ensure we get unique cache keys
- accessor.IdTokenCacheDictionary[item.GetKey().ToString()] = item;
+ accessor.SaveIdToken(item);
}
for (int i = 1; i <= NumAccounts; i++)
{
var item = CreateAccountItem();
item.Environment = item.Environment + $"_{i}"; // ensure we get unique cache keys
- accessor.AccountCacheDictionary[item.GetKey().ToString()] = item;
+ accessor.SaveAccount(item);
}
+ accessor.SaveAppMetadata(new MsalAppMetadataCacheItem(MsalTestConstants.ClientId, "env_1", "1"));
+ accessor.SaveAppMetadata(new MsalAppMetadataCacheItem(MsalTestConstants.ClientId, "env_2", ""));
+ accessor.SaveAppMetadata(new MsalAppMetadataCacheItem(MsalTestConstants.ClientId2, "env_1", "another_family"));
+
return accessor;
}
@@ -200,6 +215,29 @@ public void TestSerializeMsalRefreshTokenCacheItem()
AssertRefreshTokenCacheItemsAreEqual(item, item2);
}
+ [TestMethod]
+ public void Test_FRT_SerializeDeserialize()
+ {
+ var item1 = CreateRefreshTokenItem();
+ item1.FamilyId = null;
+ var item2 = CreateRefreshTokenItem();
+ item2.FamilyId = "";
+ var item3 = CreateRefreshTokenItem();
+ item3.FamilyId = "1";
+
+ var json1 = item1.ToJsonString();
+ var json2 = item2.ToJsonString();
+ var json3 = item3.ToJsonString();
+
+ var reserialized1 = MsalRefreshTokenCacheItem.FromJsonString(json1);
+ var reserialized2 = MsalRefreshTokenCacheItem.FromJsonString(json2);
+ var reserialized3 = MsalRefreshTokenCacheItem.FromJsonString(json3);
+
+ AssertRefreshTokenCacheItemsAreEqual(item1, reserialized1);
+ AssertRefreshTokenCacheItemsAreEqual(item2, reserialized2);
+ AssertRefreshTokenCacheItemsAreEqual(item3, reserialized3);
+ }
+
[TestMethod]
public void TestSerializeMsalRefreshTokenCacheItemWithAdditionalFields()
{
@@ -326,6 +364,43 @@ public void TestMsalAccountCacheItem_HasProperJObjectFields()
#endregion // ACCOUNT TESTS
+ #region APP METADATA TESTS
+
+ [TestMethod]
+ public void TestAppMetadata_SerializeDeserialize()
+ {
+ var item = new MsalAppMetadataCacheItem(MsalTestConstants.ClientId, "env", "1");
+ string asJson = item.ToJsonString();
+ var item2 = MsalAppMetadataCacheItem.FromJsonString(asJson);
+
+ Assert.AreEqual(item, item2);
+ }
+
+ [TestMethod]
+ public void TestAppMetadata_Supports_AdditionalFields()
+ {
+ var item = new MsalAppMetadataCacheItem(MsalTestConstants.ClientId, "env", "1");
+
+ // Add an unknown field into the json
+ var asJObject = item.ToJObject();
+ AssertContainsKeys(asJObject, s_appMetadataKeys);
+
+ asJObject["unsupported_field_name"] = "this is a value";
+
+ // Ensure unknown field remains in the AdditionalFieldsJson block
+ var item2 = MsalAppMetadataCacheItem.FromJObject(asJObject);
+ Assert.AreEqual("{\r\n \"unsupported_field_name\": \"this is a value\"\r\n}", item2.AdditionalFieldsJson);
+
+ // Ensure additional fields make the round trip into json
+ asJObject = item2.ToJObject();
+ AssertContainsKeys(asJObject, s_appMetadataKeys);
+ AssertContainsKeys(asJObject, new[] { "unsupported_field_name" });
+ }
+
+
+ #endregion // APP METADATA TESTS
+
+
#region DICTIONARY SERIALIZATION TESTS
[TestMethod]
@@ -351,21 +426,31 @@ public void TestDictionarySerialization()
#region JSON SERIALIZATION TESTS
[TestMethod]
+ [DeploymentItem(@"Resources\ExpectedTokenCache.json")]
public void TestJsonSerialization()
{
+ string expectedJson = File.ReadAllText(ResourceHelper.GetTestResourceRelativePath("ExpectedTokenCache.json"));
var accessor = CreateTokenCacheAccessor();
var s1 = new TokenCacheJsonSerializer(accessor);
byte[] bytes = s1.Serialize();
- string json = new UTF8Encoding().GetString(bytes);
+ string actualJson = new UTF8Encoding().GetString(bytes);
+
+ Assert.IsTrue(JToken.DeepEquals(JObject.Parse(actualJson), JObject.Parse(expectedJson)));
var otherAccessor = new InMemoryTokenCacheAccessor();
var s2 = new TokenCacheJsonSerializer(otherAccessor);
s2.Deserialize(bytes);
AssertAccessorsAreEqual(accessor, otherAccessor);
+
+ // serialize again to detect errors that come from deserialization
+ byte[] bytes2 = s2.Serialize();
+ string actualJson2 = new UTF8Encoding().GetString(bytes2);
+ Assert.IsTrue(JToken.DeepEquals(JObject.Parse(actualJson2), JObject.Parse(expectedJson)));
}
+
#endregion // JSON SERIALIZATION TESTS
[TestMethod]
@@ -379,10 +464,10 @@ public void TestPythonCacheSerializationInterop()
byte[] bytes = File.ReadAllBytes(pythonBinFilePath);
s.Deserialize(bytes);
- Assert.AreEqual(0, accessor.AccessTokenCount, nameof(accessor.AccessTokenCount));
- Assert.AreEqual(0, accessor.RefreshTokenCount, nameof(accessor.RefreshTokenCount));
- Assert.AreEqual(0, accessor.IdTokenCount, nameof(accessor.IdTokenCount));
- Assert.AreEqual(0, accessor.AccountCount, nameof(accessor.AccountCount));
+ Assert.AreEqual(0, accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(0, accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(0, accessor.GetAllAccounts().Count());
}
[TestMethod]
@@ -395,10 +480,11 @@ public void TestMsalNet2XCacheSerializationInterop()
byte[] bytes = File.ReadAllBytes(binFilePath);
s.Deserialize(bytes);
- Assert.AreEqual(1, accessor.AccessTokenCount, nameof(accessor.AccessTokenCount));
- Assert.AreEqual(1, accessor.RefreshTokenCount, nameof(accessor.RefreshTokenCount));
- Assert.AreEqual(1, accessor.IdTokenCount, nameof(accessor.IdTokenCount));
- Assert.AreEqual(1, accessor.AccountCount, nameof(accessor.AccountCount));
+ Assert.AreEqual(1, accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(1, accessor.GetAllAccounts().Count());
+ Assert.AreEqual(0, accessor.GetAllAppMetadata().Count());
var expectedAccessTokenItem = new MsalAccessTokenCacheItem
{
@@ -414,7 +500,7 @@ public void TestMsalNet2XCacheSerializationInterop()
NormalizedScopes = "User.Read User.ReadBasic.All profile openid email",
UserAssertionHash = string.Empty
};
- AssertAccessTokenCacheItemsAreEqual(expectedAccessTokenItem, accessor.AccessTokenCacheDictionary.Values.First());
+ AssertAccessTokenCacheItemsAreEqual(expectedAccessTokenItem, accessor.GetAllAccessTokens().First());
var expectedRefreshTokenItem = new MsalRefreshTokenCacheItem
{
@@ -423,7 +509,7 @@ public void TestMsalNet2XCacheSerializationInterop()
RawClientInfo = string.Empty,
ClientId = "b945c513-3946-4ecd-b179-6499803a2167"
};
- AssertRefreshTokenCacheItemsAreEqual(expectedRefreshTokenItem, accessor.RefreshTokenCacheDictionary.Values.First());
+ AssertRefreshTokenCacheItemsAreEqual(expectedRefreshTokenItem, accessor.GetAllRefreshTokens().First());
var expectedIdTokenItem = new MsalIdTokenCacheItem
{
@@ -433,7 +519,7 @@ public void TestMsalNet2XCacheSerializationInterop()
ClientId = "b945c513-3946-4ecd-b179-6499803a2167",
TenantId = "26039cce-489d-4002-8293-5b0c5134eacb"
};
- AssertIdTokenCacheItemsAreEqual(expectedIdTokenItem, accessor.IdTokenCacheDictionary.Values.First());
+ AssertIdTokenCacheItemsAreEqual(expectedIdTokenItem, accessor.GetAllIdTokens().First());
var expectedAccountItem = new MsalAccountCacheItem
{
@@ -447,15 +533,15 @@ public void TestMsalNet2XCacheSerializationInterop()
LocalAccountId = "13dd2c19-84cd-416a-ae7d-49573e425619",
TenantId = "26039cce-489d-4002-8293-5b0c5134eacb"
};
- AssertAccountCacheItemsAreEqual(expectedAccountItem, accessor.AccountCacheDictionary.Values.First());
+ AssertAccountCacheItemsAreEqual(expectedAccountItem, accessor.GetAllAccounts().First());
}
private void AssertAccessorsAreEqual(ITokenCacheAccessor expected, ITokenCacheAccessor actual)
{
- Assert.AreEqual(expected.AccessTokenCount, actual.AccessTokenCount);
- Assert.AreEqual(expected.RefreshTokenCount, actual.RefreshTokenCount);
- Assert.AreEqual(expected.IdTokenCount, actual.IdTokenCount);
- Assert.AreEqual(expected.AccountCount, actual.AccountCount);
+ Assert.AreEqual(expected.GetAllAccessTokens().Count(), actual.GetAllAccessTokens().Count());
+ Assert.AreEqual(expected.GetAllRefreshTokens().Count(), actual.GetAllRefreshTokens().Count());
+ Assert.AreEqual(expected.GetAllIdTokens().Count(), actual.GetAllIdTokens().Count());
+ Assert.AreEqual(expected.GetAllAccounts().Count(), actual.GetAllAccounts().Count());
}
private void AssertContainsKey(JObject j, string key)
@@ -582,6 +668,17 @@ private void AssertAccessTokenCacheItemsAreEqual(MsalAccessTokenCacheItem expect
private void AssertRefreshTokenCacheItemsAreEqual(MsalRefreshTokenCacheItem expected, MsalRefreshTokenCacheItem actual)
{
AssertCredentialCacheItemBaseItemsAreEqual(expected, actual);
+
+ if (string.IsNullOrEmpty(expected.FamilyId))
+ {
+ Assert.IsTrue(string.IsNullOrEmpty(actual.FamilyId));
+ }
+ else
+ {
+ Assert.AreEqual(expected.FamilyId, actual.FamilyId);
+ }
+
+
}
private void AssertIdTokenCacheItemsAreEqual(MsalIdTokenCacheItem expected, MsalIdTokenCacheItem actual)
@@ -603,4 +700,4 @@ private void AssertAccountCacheItemsAreEqual(MsalAccountCacheItem expected, Msal
Assert.AreEqual(expected.TenantId, actual.TenantId, nameof(actual.TenantId));
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/TokenCacheTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/TokenCacheTests.cs
index fc973de539..c4cd60eed2 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/TokenCacheTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/TokenCacheTests.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
// All rights reserved.
@@ -28,22 +28,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.ApiConfig.Parameters;
-using Microsoft.Identity.Client.Core;
-using Microsoft.Identity.Client.Internal;
-using Microsoft.Identity.Client.Internal.Requests;
using Microsoft.Identity.Client.Cache;
using Microsoft.Identity.Client.Cache.Items;
+using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.Instance;
+using Microsoft.Identity.Client.Internal.Requests;
using Microsoft.Identity.Client.OAuth2;
-using Microsoft.Identity.Client.TelemetryCore;
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
using Microsoft.Identity.Client.Utils;
+using Microsoft.Identity.Test.Common;
+using Microsoft.Identity.Test.Common.Core.Helpers;
using Microsoft.Identity.Test.Common.Core.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Microsoft.Identity.Client.PlatformsCommon.Factories;
-using Microsoft.Identity.Test.Common;
+using NSubstitute;
namespace Microsoft.Identity.Test.Unit.CacheTests
{
@@ -392,27 +391,17 @@ public void DoNotSaveRefreshTokenInAdalCacheForMsalB2CAuthorityTest()
var serviceBundle = TestCommon.CreateDefaultServiceBundle();
ITokenCacheInternal cache = new TokenCache(serviceBundle);
- var response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- AccessToken = "access-token",
- ClientInfo = MockHelpers.CreateClientInfo(),
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = MsalTestConstants.Scope.AsSingleString(),
- TokenType = "Bearer"
- };
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
var requestParams = CreateAuthenticationRequestParameters(serviceBundle, authority: Authority.CreateAuthority(serviceBundle, MsalTestConstants.B2CAuthority));
requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityTestTenant;
AddHostToInstanceCache(serviceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response);
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
IDictionary dictionary =
AdalCacheOperations.Deserialize(serviceBundle.DefaultLogger, cache.LegacyPersistence.LoadCache());
@@ -551,74 +540,137 @@ public void SaveAccessAndRefreshTokenWithEmptyCacheTest()
var serviceBundle = TestCommon.CreateDefaultServiceBundle();
ITokenCacheInternal cache = new TokenCache(serviceBundle);
- var response = new MsalTokenResponse
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
+
+ var requestParams = CreateAuthenticationRequestParameters(serviceBundle);
+ requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityTestTenant;
+
+ AddHostToInstanceCache(serviceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
+
+ cache.SaveTokenResponse(requestParams, response);
+
+ cache.Accessor.AssertItemCount(
+ expectedAtCount: 1,
+ expectedRtCount: 1,
+ expectedAccountCount: 1,
+ expectedIdtCount: 1,
+ expectedAppMetadataCount: 1);
+
+ var metadata = cache.Accessor.GetAllAppMetadata().First();
+ Assert.AreEqual(MsalTestConstants.ClientId, metadata.ClientId);
+ Assert.AreEqual(MsalTestConstants.ProductionPrefNetworkEnvironment, metadata.Environment);
+ Assert.IsNull(metadata.FamilyId);
+ }
+
+ [TestMethod]
+ public void NoAppMetadata_WhenFociIsDisabled()
+ {
+ using (var harness = new MockHttpAndServiceBundle())
{
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- AccessToken = "access-token",
- ClientInfo = MockHelpers.CreateClientInfo(),
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = MsalTestConstants.Scope.AsSingleString(),
- TokenType = "Bearer"
- };
+ // Arrange
+ var testFlags = Substitute.For();
+ testFlags.IsFociEnabled.Returns(false);
+
+ harness.ServiceBundle.PlatformProxy.SetFeatureFlags(testFlags);
+
+ ITokenCacheInternal cache = new TokenCache(harness.ServiceBundle);
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
+ var requestParams = CreateAuthenticationRequestParameters(harness.ServiceBundle);
+ requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityTestTenant;
+ AddHostToInstanceCache(harness.ServiceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
+
+ // Act
+ cache.SaveTokenResponse(requestParams, response);
+
+ // Assert
+ cache.Accessor.AssertItemCount(
+ expectedAtCount: 1,
+ expectedRtCount: 1,
+ expectedAccountCount: 1,
+ expectedIdtCount: 1,
+ expectedAppMetadataCount: 0);
+
+ // Don't save RT as an FRT if FOCI is disabled
+ Assert.IsTrue(string.IsNullOrEmpty(cache.Accessor.GetAllRefreshTokens().First().FamilyId));
+ }
+ }
+
+ [TestMethod]
+ public void SaveMultipleAppmetadata()
+ {
+ var serviceBundle = TestCommon.CreateDefaultServiceBundle();
+ ITokenCacheInternal cache = new TokenCache(serviceBundle);
+
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
+ MsalTokenResponse response2 = MsalTestConstants.CreateMsalTokenResponse();
+ response2.FamilyId = "1";
var requestParams = CreateAuthenticationRequestParameters(serviceBundle);
requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityTestTenant;
AddHostToInstanceCache(serviceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response2);
+
+ cache.Accessor.AssertItemCount(
+ expectedAtCount: 1,
+ expectedRtCount: 2, // a normal RT and an FRT
+ expectedAccountCount: 1,
+ expectedIdtCount: 1,
+ expectedAppMetadataCount: 1);
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ var metadata = cache.Accessor.GetAllAppMetadata().First();
+ Assert.AreEqual(MsalTestConstants.ClientId, metadata.ClientId);
+ Assert.AreEqual(MsalTestConstants.ProductionPrefNetworkEnvironment, metadata.Environment);
+ Assert.AreEqual(MsalTestConstants.FamilyId, metadata.FamilyId);
+
+ Assert.IsTrue(cache.Accessor.GetAllRefreshTokens().Any(rt => rt.FamilyId == "1"));
+ Assert.IsTrue(cache.Accessor.GetAllRefreshTokens().Any(rt => string.IsNullOrEmpty(rt.FamilyId)));
}
+
+ [TestMethod]
+ public void CreateFrtFromTokenResponse()
+ {
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
+ response.FamilyId = "1";
+
+ var frt = new MsalRefreshTokenCacheItem("env", MsalTestConstants.ClientId, response);
+
+ Assert.AreEqual("1", frt.FamilyId);
+ }
+
+
[TestMethod]
[TestCategory("TokenCacheTests")]
public void SaveAccessAndRefreshTokenWithMoreScopesTest()
{
var serviceBundle = TestCommon.CreateDefaultServiceBundle();
ITokenCacheInternal cache = new TokenCache(serviceBundle);
-
- var response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = MsalTestConstants.Scope.AsSingleString(),
- TokenType = "Bearer"
- };
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
var requestParams = CreateAuthenticationRequestParameters(serviceBundle);
requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityTestTenant;
AddHostToInstanceCache(serviceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response);
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
- response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token-2",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token-2",
- Scope = MsalTestConstants.Scope.AsSingleString() + " another-scope",
- TokenType = "Bearer"
- };
+ Assert.IsNull(cache.Accessor.GetAllRefreshTokens().First().FamilyId);
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ response = MsalTestConstants.CreateMsalTokenResponse();
+ response.Scope = MsalTestConstants.Scope.AsSingleString() + " another-scope";
+ response.AccessToken = "access-token-2";
+ response.RefreshToken = "refresh-token-2";
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ cache.SaveTokenResponse(requestParams, response);
+
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
Assert.AreEqual("refresh-token-2", cache.GetAllRefreshTokens(true).First().Secret);
Assert.AreEqual("access-token-2", cache.GetAllAccessTokens(true).First().Secret);
@@ -630,42 +682,24 @@ public void SaveAccessAndRefreshTokenWithLessScopesTest()
{
var serviceBundle = TestCommon.CreateDefaultServiceBundle();
ITokenCacheInternal cache = new TokenCache(serviceBundle);
-
- var response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = MsalTestConstants.Scope.AsSingleString(),
- TokenType = "Bearer"
- };
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
var requestParams = CreateAuthenticationRequestParameters(serviceBundle);
requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityTestTenant;
AddHostToInstanceCache(serviceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response);
- response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token-2",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token-2",
- Scope = MsalTestConstants.Scope.First(),
- TokenType = "Bearer"
- };
+ response = MsalTestConstants.CreateMsalTokenResponse();
+ response.Scope = MsalTestConstants.Scope.First();
+ response.AccessToken = "access-token-2";
+ response.RefreshToken = "refresh-token-2";
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response);
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
Assert.AreEqual("refresh-token-2", cache.GetAllRefreshTokens(true).First().Secret);
Assert.AreEqual("access-token-2", cache.GetAllAccessTokens(true).First().Secret);
}
@@ -676,42 +710,24 @@ public void SaveAccessAndRefreshTokenWithIntersectingScopesTest()
{
var serviceBundle = TestCommon.CreateDefaultServiceBundle();
ITokenCacheInternal cache = new TokenCache(serviceBundle);
-
- var response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- AccessToken = "access-token",
- ClientInfo = MockHelpers.CreateClientInfo(),
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = MsalTestConstants.Scope.AsSingleString(),
- TokenType = "Bearer"
- };
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
var requestParams = CreateAuthenticationRequestParameters(serviceBundle);
requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityTestTenant;
AddHostToInstanceCache(serviceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response);
- response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token-2",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token-2",
- Scope = MsalTestConstants.Scope.AsSingleString() + " random-scope",
- TokenType = "Bearer"
- };
+ response = MsalTestConstants.CreateMsalTokenResponse();
+ response.Scope = MsalTestConstants.Scope.AsSingleString() + " random-scope";
+ response.AccessToken = "access-token-2";
+ response.RefreshToken = "refresh-token-2";
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response);
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
Assert.AreEqual("refresh-token-2", cache.GetAllRefreshTokens(true).First().Secret);
Assert.AreEqual("access-token-2", cache.GetAllAccessTokens(true).First().Secret);
@@ -743,49 +759,30 @@ public void SaveAccessAndRefreshTokenWithDifferentAuthoritySameUserTest()
{
var serviceBundle = TestCommon.CreateDefaultServiceBundle();
ITokenCacheInternal cache = new TokenCache(serviceBundle);
-
- var response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = MsalTestConstants.Scope.AsSingleString(),
- TokenType = "Bearer"
- };
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
var requestParams = CreateAuthenticationRequestParameters(serviceBundle);
requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityHomeTenant;
AddHostToInstanceCache(serviceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
- cache.SaveAccessAndRefreshToken(requestParams, response);
-
- response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token-2",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token-2",
- Scope = MsalTestConstants.Scope.AsSingleString() + " another-scope",
- TokenType = "Bearer"
- };
+ cache.SaveTokenResponse(requestParams, response);
+ response = MsalTestConstants.CreateMsalTokenResponse();
+ response.Scope = MsalTestConstants.Scope.AsSingleString() + " another-scope";
+ response.AccessToken = "access-token-2";
+ response.RefreshToken = "refresh-token-2";
requestParams = CreateAuthenticationRequestParameters(serviceBundle);
requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityGuestTenant;
- ((ITokenCache)cache).SetAfterAccess(AfterAccessChangedNotification);
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SetAfterAccess(AfterAccessChangedNotification);
+ cache.SaveTokenResponse(requestParams, response);
#pragma warning disable CS0618 // Type or member is obsolete
Assert.IsFalse(((TokenCache)cache).HasStateChanged);
#pragma warning restore CS0618 // Type or member is obsolete
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(2, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(2, cache.Accessor.GetAllAccessTokens().Count());
Assert.AreEqual("refresh-token-2", cache.GetAllRefreshTokens(true).First().Secret);
}
@@ -812,17 +809,7 @@ public void SerializeDeserializeCacheTest()
var serviceBundle = TestCommon.CreateDefaultServiceBundle();
ITokenCacheInternal cache = new TokenCache(serviceBundle);
- var response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(MsalTestConstants.UniqueId, MsalTestConstants.DisplayableId),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = MsalTestConstants.Scope.AsSingleString(),
- TokenType = "Bearer"
- };
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
var requestContext = RequestContext.CreateForTest(serviceBundle);
var requestParams = CreateAuthenticationRequestParameters(serviceBundle, requestContext: requestContext);
@@ -830,29 +817,27 @@ public void SerializeDeserializeCacheTest()
AddHostToInstanceCache(serviceBundle, MsalTestConstants.ProductionPrefNetworkEnvironment);
- cache.SaveAccessAndRefreshToken(requestParams, response);
- byte[] serializedCache = ((ITokenCache)cache).SerializeMsalV3();
-
- string cacheString = new UTF8Encoding().GetString(serializedCache);
+ cache.SaveTokenResponse(requestParams, response);
+ byte[] serializedCache = cache.SerializeMsalV3();
cache.Accessor.ClearAccessTokens();
cache.Accessor.ClearRefreshTokens();
- Assert.AreEqual(0, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(0, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(0, cache.Accessor.GetAllAccessTokens().Count());
- ((ITokenCache)cache).DeserializeMsalV3(serializedCache);
+ cache.DeserializeMsalV3(serializedCache);
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
- serializedCache = ((ITokenCache)cache).SerializeMsalV3();
- ((ITokenCache)cache).DeserializeMsalV3(serializedCache);
+ serializedCache = cache.SerializeMsalV3();
+ cache.DeserializeMsalV3(serializedCache);
// item count should not change because old cache entries should have
// been overriden
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
var atItem = cache.GetAllAccessTokens(true).First();
Assert.AreEqual(response.AccessToken, atItem.Secret);
@@ -907,31 +892,21 @@ public void CacheB2CTokenTest()
$"https://login.microsoftonline.com/tfp/{tenantID}/somePolicy/oauth2/v2.0/authorize");
// creating IDToken with empty tenantID and displayableID/PreferredUserName for B2C scenario
- var response = new MsalTokenResponse
- {
- IdToken = MockHelpers.CreateIdToken(string.Empty, string.Empty, string.Empty),
- ClientInfo = MockHelpers.CreateClientInfo(),
- AccessToken = "access-token",
- ExpiresIn = 3599,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = MsalTestConstants.Scope.AsSingleString(),
- TokenType = "Bearer"
- };
+ MsalTokenResponse response = MsalTestConstants.CreateMsalTokenResponse();
var requestContext = RequestContext.CreateForTest(serviceBundle);
var requestParams = CreateAuthenticationRequestParameters(serviceBundle, authority, requestContext: requestContext);
requestParams.TenantUpdatedCanonicalAuthority = MsalTestConstants.AuthorityTestTenant;
- cache.SaveAccessAndRefreshToken(requestParams, response);
+ cache.SaveTokenResponse(requestParams, response);
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
}
private AuthenticationRequestParameters CreateAuthenticationRequestParameters(
- IServiceBundle serviceBundle,
- Authority authority = null,
+ IServiceBundle serviceBundle,
+ Authority authority = null,
SortedSet scopes = null,
RequestContext requestContext = null)
{
@@ -955,6 +930,90 @@ public void TestCacheDeserializeWithoutServiceBundle()
{
var tokenCache = new TokenCache();
tokenCache.DeserializeMsalV3(new byte[0]);
+ }
+
+ [TestMethod]
+ public void TestIsFociMember()
+ {
+ // Arrange
+ using (var harness = new MockHttpAndServiceBundle())
+ {
+ harness.HttpManager.AddInstanceDiscoveryMockHandler();
+ ITokenCacheInternal cache = new TokenCache(harness.ServiceBundle);
+ AuthenticationRequestParameters requestParams = harness.CreateAuthenticationRequestParameters(
+ MsalTestConstants.AuthorityTestTenant,
+ MsalTestConstants.Scope,
+ account: MsalTestConstants.User);
+
+ // Act
+ bool? result = cache.IsFociMemberAsync(requestParams, "1").Result;
+
+ // Assert
+ Assert.IsNull(result, "No app metadata, should return null which indicates ");
+
+ ValidateIsFociMember(cache, requestParams,
+ metadataFamilyId: "1",
+ expectedResult: true, // checks for familyId "1"
+ errMessage: "Valid app metadata, should return true because family Id matches");
+
+
+ ValidateIsFociMember(cache, requestParams,
+ metadataFamilyId: "2",
+ expectedResult: false, // checks for familyId "1"
+ errMessage: "Valid app metadata, should return false because family Id does not match");
+
+
+ ValidateIsFociMember(cache, requestParams,
+ metadataFamilyId: null,
+ expectedResult: false, // checks for familyId "1"
+ errMessage: "Valid app metadata showing that the app is not member of any family");
+ }
+ }
+
+ [TestMethod]
+ public void TestIsFociMember_EnvAlias()
+ {
+ // Arrange
+ using (var harness = new MockHttpAndServiceBundle())
+ {
+ harness.HttpManager.AddInstanceDiscoveryMockHandler();
+ ITokenCacheInternal cache = new TokenCache(harness.ServiceBundle);
+ AuthenticationRequestParameters requestParams = harness.CreateAuthenticationRequestParameters(
+ MsalTestConstants.AuthorityTestTenant,
+ MsalTestConstants.Scope,
+ account: MsalTestConstants.User);
+
+ cache.Accessor.SaveAppMetadata(
+ new MsalAppMetadataCacheItem(
+ MsalTestConstants.ClientId,
+ MsalTestConstants.ProductionNotPrefEnvironmentAlias,
+ "1"));
+
+ // Act
+ bool? result = cache.IsFociMemberAsync(requestParams, "1").Result; //requst params uses ProductionPrefEnvAlias
+
+ // Assert
+ Assert.AreEqual(true, result.Value);
+ }
+ }
+
+ private void ValidateIsFociMember(
+ ITokenCacheInternal cache,
+ AuthenticationRequestParameters requestParams,
+ string metadataFamilyId,
+ bool? expectedResult,
+ string errMessage)
+ {
+ // Arrange
+ var metadata = new MsalAppMetadataCacheItem(MsalTestConstants.ClientId, MsalTestConstants.ProductionPrefCacheEnvironment, metadataFamilyId);
+ cache.Accessor.SaveAppMetadata(metadata);
+
+ // Act
+ var result = cache.IsFociMemberAsync(requestParams, "1").Result;
+
+ // Assert
+ Assert.AreEqual(expectedResult, result, errMessage);
+ Assert.AreEqual(1, cache.Accessor.GetAllAppMetadata().Count());
}
/*
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/UnifiedCacheFormatTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/UnifiedCacheFormatTests.cs
index edf3a83f56..70b487a540 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/UnifiedCacheFormatTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/CacheTests/UnifiedCacheFormatTests.cs
@@ -33,9 +33,8 @@
using System.Net.Http;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.AppConfig;
+using Microsoft.Identity.Client.Cache.Keys;
using Microsoft.Identity.Client.Core;
-using Microsoft.Identity.Client.Internal;
-using Microsoft.Identity.Client.Instance;
using Microsoft.Identity.Client.UI;
using Microsoft.Identity.Client.Utils;
using Microsoft.Identity.Test.Common;
@@ -65,35 +64,35 @@ private void TestInitialize(MockHttpManager httpManager)
MsalTestConstants.GetDiscoveryEndpoint(MsalTestConstants.AuthorityCommonTenant)));
}
- private string ClientId;
- private string RequestAuthority;
+ private string _clientId;
+ private string _requestAuthority;
- private string TokenResponse;
- private string IdTokenResponse;
+ private string _tokenResponse;
+ private string _idTokenResponse;
- private string ExpectedAtCacheKey;
- private string ExpectedAtCacheKeyIosService;
- private string ExpectedAtCacheKeyIosAccount;
- private string ExpectedAtCacheKeyIosGeneric;
- private string ExpectedAtCacheValue;
+ private string _expectedAtCacheKey;
+ private string _expectedAtCacheKeyIosService;
+ private string _expectedAtCacheKeyIosAccount;
+ private string _expectedAtCacheKeyIosGeneric;
+ private string _expectedAtCacheValue;
- private string ExpectedIdTokenCacheKey;
- private string ExpectedIdTokenCacheKeyIosService;
- private string ExpectedIdTokenCacheKeyIosAccount;
- private string ExpectedIdTokenCacheKeyIosGeneric;
- private string ExpectedIdTokenCacheValue;
+ private string _expectedIdTokenCacheKey;
+ private string _expectedIdTokenCacheKeyIosService;
+ private string _expectedIdTokenCacheKeyIosAccount;
+ private string _expectedIdTokenCacheKeyIosGeneric;
+ private string _expectedIdTokenCacheValue;
- private string ExpectedRtCacheKey;
- private string ExpectedRtCacheKeyIosService;
- private string ExpectedRtCacheKeyIosAccount;
- private string ExpectedRtCacheKeyIosGeneric;
- private string ExpectedRtCacheValue;
+ private string _expectedRtCacheKey;
+ private string _expectedRtCacheKeyIosService;
+ private string _expectedRtCacheKeyIosAccount;
+ private string _expectedRtCacheKeyIosGeneric;
- private string ExpectedAccountCacheKey;
- private string ExpectedAccountCacheKeyIosService;
- private string ExpectedAccountCacheKeyIosAccount;
- private string ExpectedAccountCacheKeyIosGeneric;
- private string ExpectedAccountCacheValue;
+ private string _expectedAccountCacheKey;
+ private string _expectedAccountCacheKeyIosService;
+ private string _expectedAccountCacheKeyIosAccount;
+ private string _expectedAccountCacheKeyIosGeneric;
+ private string _expectedAccountCacheValue;
+ private string _expectedRtCacheValue;
private readonly RequestContext requestContext = RequestContext.CreateForTest();
@@ -104,45 +103,45 @@ private void IntitTestData(string fileName)
string json = r.ReadToEnd();
var configJson = JsonConvert.DeserializeObject(json);
- ClientId = configJson.GetValue("client_id").ToString();
- RequestAuthority = configJson.GetValue("authority").ToString();
+ _clientId = configJson.GetValue("client_id").ToString();
+ _requestAuthority = configJson.GetValue("authority").ToString();
- TokenResponse = configJson.GetValue("token_response").ToString();
- IdTokenResponse = configJson.GetValue("id_token_response").ToString();
+ _tokenResponse = configJson.GetValue("token_response").ToString();
+ _idTokenResponse = configJson.GetValue("id_token_response").ToString();
- ExpectedAtCacheKey = configJson.GetValue("at_cache_key").ToString();
- ExpectedAtCacheKeyIosService = configJson.GetValue("at_cache_key_ios_service").ToString();
- ExpectedAtCacheKeyIosAccount = configJson.GetValue("at_cache_key_ios_account").ToString();
- ExpectedAtCacheKeyIosGeneric = configJson.GetValue("at_cache_key_ios_generic").ToString();
- ExpectedAtCacheKey = configJson.GetValue("at_cache_key").ToString();
+ _expectedAtCacheKey = configJson.GetValue("at_cache_key").ToString();
+ _expectedAtCacheKeyIosService = configJson.GetValue("at_cache_key_ios_service").ToString();
+ _expectedAtCacheKeyIosAccount = configJson.GetValue("at_cache_key_ios_account").ToString();
+ _expectedAtCacheKeyIosGeneric = configJson.GetValue("at_cache_key_ios_generic").ToString();
+ _expectedAtCacheKey = configJson.GetValue("at_cache_key").ToString();
- ExpectedAtCacheValue = configJson.GetValue("at_cache_value").ToString();
+ _expectedAtCacheValue = configJson.GetValue("at_cache_value").ToString();
- ExpectedIdTokenCacheKey = configJson.GetValue("id_token_cache_key").ToString();
- ExpectedIdTokenCacheKeyIosService = configJson.GetValue("id_token_cache_key_ios_service").ToString();
- ExpectedIdTokenCacheKeyIosAccount = configJson.GetValue("id_token_cache_key_ios_account").ToString();
- ExpectedIdTokenCacheKeyIosGeneric = configJson.GetValue("id_token_cache_key_ios_generic").ToString();
- ExpectedIdTokenCacheValue = configJson.GetValue("id_token_cache_value").ToString();
+ _expectedIdTokenCacheKey = configJson.GetValue("id_token_cache_key").ToString();
+ _expectedIdTokenCacheKeyIosService = configJson.GetValue("id_token_cache_key_ios_service").ToString();
+ _expectedIdTokenCacheKeyIosAccount = configJson.GetValue("id_token_cache_key_ios_account").ToString();
+ _expectedIdTokenCacheKeyIosGeneric = configJson.GetValue("id_token_cache_key_ios_generic").ToString();
+ _expectedIdTokenCacheValue = configJson.GetValue("id_token_cache_value").ToString();
- ExpectedRtCacheKey = configJson.GetValue("rt_cache_key").ToString();
- ExpectedRtCacheKeyIosService = configJson.GetValue("rt_cache_key_ios_service").ToString();
- ExpectedRtCacheKeyIosAccount = configJson.GetValue("rt_cache_key_ios_account").ToString();
- ExpectedRtCacheKeyIosGeneric = configJson.GetValue("rt_cache_key_ios_generic").ToString();
- ExpectedRtCacheValue = configJson.GetValue("rt_cache_value").ToString();
+ _expectedRtCacheKey = configJson.GetValue("rt_cache_key").ToString();
+ _expectedRtCacheKeyIosService = configJson.GetValue("rt_cache_key_ios_service").ToString();
+ _expectedRtCacheKeyIosAccount = configJson.GetValue("rt_cache_key_ios_account").ToString();
+ _expectedRtCacheKeyIosGeneric = configJson.GetValue("rt_cache_key_ios_generic").ToString();
+ _expectedRtCacheValue = configJson.GetValue("rt_cache_value").ToString();
- ExpectedAccountCacheKey = configJson.GetValue("account_cache_key").ToString();
- ExpectedAccountCacheKeyIosService = configJson.GetValue("account_cache_key_ios_service").ToString();
- ExpectedAccountCacheKeyIosAccount = configJson.GetValue("account_cache_key_ios_account").ToString();
- ExpectedAccountCacheKeyIosGeneric = configJson.GetValue("account_cache_key_ios_generic").ToString();
- ExpectedAccountCacheValue = configJson.GetValue("account_cache_value").ToString();
+ _expectedAccountCacheKey = configJson.GetValue("account_cache_key").ToString();
+ _expectedAccountCacheKeyIosService = configJson.GetValue("account_cache_key_ios_service").ToString();
+ _expectedAccountCacheKeyIosAccount = configJson.GetValue("account_cache_key_ios_account").ToString();
+ _expectedAccountCacheKeyIosGeneric = configJson.GetValue("account_cache_key_ios_generic").ToString();
+ _expectedAccountCacheValue = configJson.GetValue("account_cache_value").ToString();
- var idTokenSecret = CreateIdToken(IdTokenResponse);
+ var idTokenSecret = CreateIdToken(_idTokenResponse);
- TokenResponse = string.Format
- (CultureInfo.InvariantCulture, "{" + TokenResponse + "}", idTokenSecret);
+ _tokenResponse = string.Format
+ (CultureInfo.InvariantCulture, "{" + _tokenResponse + "}", idTokenSecret);
- ExpectedIdTokenCacheValue = string.Format
- (CultureInfo.InvariantCulture, "{" + ExpectedIdTokenCacheValue + "}", idTokenSecret);
+ _expectedIdTokenCacheValue = string.Format
+ (CultureInfo.InvariantCulture, "{" + _expectedIdTokenCacheValue + "}", idTokenSecret);
}
}
@@ -194,8 +193,8 @@ public void RunCacheFormatValidation()
TestInitialize(harness.HttpManager);
PublicClientApplication app = PublicClientApplicationBuilder
- .Create(ClientId)
- .WithAuthority(new Uri(RequestAuthority), true)
+ .Create(_clientId)
+ .WithAuthority(new Uri(_requestAuthority), true)
.WithHttpManager(harness.HttpManager)
.BuildConcrete();
@@ -213,7 +212,7 @@ public void RunCacheFormatValidation()
harness.HttpManager.AddMockHandler(new MockHttpMessageHandler
{
ExpectedMethod = HttpMethod.Post,
- ResponseMessage = MockHelpers.CreateSuccessResponseMessage(TokenResponse)
+ ResponseMessage = MockHelpers.CreateSuccessResponseMessage(_tokenResponse)
});
AuthenticationResult result = app.AcquireTokenAsync(MsalTestConstants.Scope).Result;
@@ -232,7 +231,7 @@ private void ValidateAt(ITokenCacheInternal cache)
Assert.AreEqual(1, atList.Count);
var actualPayload = JsonConvert.DeserializeObject(atList.First().ToJsonString());
- var expectedPayload = JsonConvert.DeserializeObject(ExpectedAtCacheValue);
+ var expectedPayload = JsonConvert.DeserializeObject(_expectedAtCacheValue);
foreach (KeyValuePair prop in expectedPayload)
{
@@ -256,11 +255,13 @@ private void ValidateAt(ITokenCacheInternal cache)
var atCacheItem = cache.GetAllAccessTokens(true).First();
var key = atCacheItem.GetKey();
- Assert.AreEqual(ExpectedAtCacheKey, key.ToString());
+ Assert.AreEqual(_expectedAtCacheKey, key.ToString());
- Assert.AreEqual(ExpectedAtCacheKeyIosService, key.GetiOSServiceKey());
- Assert.AreEqual(ExpectedAtCacheKeyIosAccount, key.GetiOSAccountKey());
- Assert.AreEqual(ExpectedAtCacheKeyIosGeneric, key.GetiOSGenericKey());
+ Assert.AreEqual(_expectedAtCacheKeyIosService, key.iOSService);
+ Assert.AreEqual(_expectedAtCacheKeyIosAccount, key.iOSAccount);
+ Assert.AreEqual(_expectedAtCacheKeyIosGeneric, key.iOSGeneric);
+ Assert.AreEqual(_expectedAtCacheKeyIosGeneric, key.iOSGeneric);
+ Assert.AreEqual((int)MsalCacheKeys.iOSCredentialAttrType.AccessToken, key.iOSType);
}
private void ValidateRt(ITokenCacheInternal cache)
@@ -272,11 +273,13 @@ private void ValidateRt(ITokenCacheInternal cache)
var rtCacheItem = cache.GetAllRefreshTokens(true).First();
var key = rtCacheItem.GetKey();
- Assert.AreEqual(ExpectedRtCacheKey, key.ToString());
+ Assert.AreEqual(_expectedRtCacheKey, key.ToString());
+
+ Assert.AreEqual(_expectedRtCacheKeyIosService, key.iOSService);
+ Assert.AreEqual(_expectedRtCacheKeyIosAccount, key.iOSAccount);
+ Assert.AreEqual(_expectedRtCacheKeyIosGeneric, key.iOSGeneric);
+ Assert.AreEqual((int)MsalCacheKeys.iOSCredentialAttrType.RefreshToken, key.iOSType);
- Assert.AreEqual(ExpectedRtCacheKeyIosService, key.GetiOSServiceKey());
- Assert.AreEqual(ExpectedRtCacheKeyIosAccount, key.GetiOSAccountKey());
- Assert.AreEqual(ExpectedRtCacheKeyIosGeneric, key.GetiOSGenericKey());
}
private void ValidateIdToken(ITokenCacheInternal cache)
@@ -288,11 +291,13 @@ private void ValidateIdToken(ITokenCacheInternal cache)
var idTokenCacheItem = cache.GetAllIdTokens(true).First();
var key = idTokenCacheItem.GetKey();
- Assert.AreEqual(ExpectedIdTokenCacheKey, key.ToString());
+ Assert.AreEqual(_expectedIdTokenCacheKey, key.ToString());
+
+ Assert.AreEqual(_expectedIdTokenCacheKeyIosService, key.iOSService);
+ Assert.AreEqual(_expectedIdTokenCacheKeyIosAccount, key.iOSAccount);
+ Assert.AreEqual(_expectedIdTokenCacheKeyIosGeneric, key.iOSGeneric);
+ Assert.AreEqual((int)MsalCacheKeys.iOSCredentialAttrType.IdToken, key.iOSType);
- Assert.AreEqual(ExpectedIdTokenCacheKeyIosService, key.GetiOSServiceKey());
- Assert.AreEqual(ExpectedIdTokenCacheKeyIosAccount, key.GetiOSAccountKey());
- Assert.AreEqual(ExpectedIdTokenCacheKeyIosGeneric, key.GetiOSGenericKey());
}
private void ValidateAccount(ITokenCacheInternal cache)
@@ -304,11 +309,13 @@ private void ValidateAccount(ITokenCacheInternal cache)
var accountCacheItem = cache.GetAllAccounts().First();
var key = accountCacheItem.GetKey();
- Assert.AreEqual(ExpectedAccountCacheKey, key.ToString());
+ Assert.AreEqual(_expectedAccountCacheKey, key.ToString());
+
+ Assert.AreEqual(_expectedAccountCacheKeyIosService, key.iOSService);
+ Assert.AreEqual(_expectedAccountCacheKeyIosAccount, key.iOSAccount);
+ Assert.AreEqual(_expectedAccountCacheKeyIosGeneric, key.iOSGeneric);
+ Assert.AreEqual(MsalCacheKeys.iOSAuthorityTypeToAttrType["MSSTS"], key.iOSType);
- Assert.AreEqual(ExpectedAccountCacheKeyIosService, key.GetiOSServiceKey());
- Assert.AreEqual(ExpectedAccountCacheKeyIosAccount, key.GetiOSAccountKey());
- Assert.AreEqual(ExpectedAccountCacheKeyIosGeneric, key.GetiOSGenericKey());
}
private void ValidateCacheEntityValue(string expectedEntityValue, ICollection entities)
@@ -331,4 +338,4 @@ private void ValidateCacheEntityValue(string expectedEntityValue, ICollection(CacheFallbackOperations.DifferentEnvError));
}
+
+ [TestMethod]
+ public void DoNotWriteFRTs()
+ {
+ // Arrange
+ _legacyCachePersistence.ThrowOnWrite = true;
+
+ var rtItem = new MsalRefreshTokenCacheItem(
+ MsalTestConstants.ProductionPrefNetworkEnvironment,
+ MsalTestConstants.ClientId,
+ "someRT",
+ MockHelpers.CreateClientInfo("u1", "ut1"),
+ "familyId");
+
+ var idTokenCacheItem = new MsalIdTokenCacheItem(
+ MsalTestConstants.ProductionPrefNetworkEnvironment, // different env
+ MsalTestConstants.ClientId,
+ MockHelpers.CreateIdToken("u1", "username"),
+ MockHelpers.CreateClientInfo("u1", "ut1"),
+ "ut1");
+
+ // Act
+ CacheFallbackOperations.WriteAdalRefreshToken(
+ _logger,
+ _legacyCachePersistence,
+ rtItem,
+ idTokenCacheItem,
+ "https://some_env.com/common", // yet another env
+ "uid",
+ "scope1");
+
+ AssertCacheEntryCount(0);
+ }
+
private void PopulateLegacyCache(ILegacyCachePersistence legacyCachePersistence)
{
PopulateLegacyWithRtAndId(
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/CoreTests/CacheTests/MsalTokenCacheKeysTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/CoreTests/CacheTests/MsalTokenCacheKeysTests.cs
index a4bc6c85ed..9e9dfce6d0 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/CoreTests/CacheTests/MsalTokenCacheKeysTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/CoreTests/CacheTests/MsalTokenCacheKeysTests.cs
@@ -26,10 +26,7 @@
//------------------------------------------------------------------------------
using System;
-using System.Linq;
-using Microsoft.Identity.Client.Cache;
using Microsoft.Identity.Client.Cache.Keys;
-using Microsoft.Identity.Test.Common;
using Microsoft.Identity.Test.Common.Core.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -41,10 +38,10 @@ public class MsalTokenCacheKeysTests
[TestMethod]
public void ArgNull()
{
- AssertException.Throws(() => new MsalRefreshTokenCacheKey("", "clientId", "uid"));
- AssertException.Throws(() => new MsalRefreshTokenCacheKey(null, "clientId", "uid"));
- AssertException.Throws(() => new MsalRefreshTokenCacheKey(null, "clientId", "uid"));
- AssertException.Throws(() => new MsalRefreshTokenCacheKey("env", null, "uid"));
+ AssertException.Throws(() => new MsalRefreshTokenCacheKey("", "clientId", "uid", "1"));
+ AssertException.Throws(() => new MsalRefreshTokenCacheKey(null, "clientId", "uid", "1"));
+ AssertException.Throws(() => new MsalRefreshTokenCacheKey(null, "clientId", "uid", "1"));
+ AssertException.Throws(() => new MsalRefreshTokenCacheKey("env", null, "uid", "1"));
AssertException.Throws(() => new MsalIdTokenCacheKey("", "tid", "uid", "cid"));
AssertException.Throws(() => new MsalIdTokenCacheKey(null, "tid", "uid", "cid"));
@@ -56,74 +53,90 @@ public void ArgNull()
AssertException.Throws(() => new MsalAccessTokenCacheKey("env", "tid", "uid", "", "scopes"));
AssertException.Throws(() => new MsalAccessTokenCacheKey("env", "tid", "uid", null, "scopes"));
- AssertException.Throws(() => new MsalAccountCacheKey("", "tid", "uid", "localid"));
- AssertException.Throws(() => new MsalAccountCacheKey(null, "tid", "uid", "localid"));
+ AssertException.Throws(() => new MsalAccountCacheKey("", "tid", "uid", "localid", "aad"));
+ AssertException.Throws(() => new MsalAccountCacheKey(null, "tid", "uid", "localid", "msa"));
}
[TestMethod]
public void MsalAccessTokenCacheKey()
{
- MsalAccessTokenCacheKey key = new MsalAccessTokenCacheKey("login.microsoftonline.com", "contoso.com", "uid.utid", "clientid", "user.read user.write");
+ var key = new MsalAccessTokenCacheKey("login.microsoftonline.com", "contoso.com", "uid.utid", "clientid", "user.read user.write");
Assert.AreEqual("uid.utid-login.microsoftonline.com-accesstoken-clientid-contoso.com-user.read user.write", key.ToString());
- Assert.AreEqual("uid.utid-login.microsoftonline.com", key.GetiOSAccountKey());
- Assert.AreEqual("accesstoken-clientid-contoso.com-user.read user.write", key.GetiOSServiceKey());
- Assert.AreEqual("accesstoken-clientid-contoso.com", key.GetiOSGenericKey());
- var serviceBundle = TestCommon.CreateDefaultServiceBundle();
- Assert.AreEqual("uid.utid-m7wizgxzfro0k4ytgwbclbecpmuf5trhsuba0vptum8=-accesstoken-clientid-contoso.com-n5wvhdusof/wfsjgk1muxrk89nwfynymsl4qefkynbu=", key.GetUWPFixedSizeKey(serviceBundle.PlatformProxy.CryptographyManager));
+ Assert.AreEqual("uid.utid-login.microsoftonline.com", key.iOSAccount);
+ Assert.AreEqual("accesstoken-clientid-contoso.com-user.read user.write", key.iOSService);
+ Assert.AreEqual("accesstoken-clientid-contoso.com", key.iOSGeneric);
+ Assert.AreEqual((int)MsalCacheKeys.iOSCredentialAttrType.AccessToken, key.iOSType);
}
[TestMethod]
public void MsalRefreshTokenCacheKey()
{
- MsalRefreshTokenCacheKey key = new MsalRefreshTokenCacheKey("login.microsoftonline.com", "clientid", "uid.utid");
+ var key = new MsalRefreshTokenCacheKey("login.microsoftonline.com", "clientid", "uid.utid", "");
Assert.AreEqual("uid.utid-login.microsoftonline.com-refreshtoken-clientid--", key.ToString());
- Assert.AreEqual("uid.utid-login.microsoftonline.com", key.GetiOSAccountKey());
- Assert.AreEqual("refreshtoken-clientid--", key.GetiOSServiceKey());
- Assert.AreEqual("refreshtoken-clientid-", key.GetiOSGenericKey());
+ Assert.AreEqual("uid.utid-login.microsoftonline.com", key.iOSAccount);
+ Assert.AreEqual("refreshtoken-clientid--", key.iOSService);
+ Assert.AreEqual("refreshtoken-clientid-", key.iOSGeneric);
+ Assert.AreEqual((int)MsalCacheKeys.iOSCredentialAttrType.RefreshToken, key.iOSType);
}
[TestMethod]
- public void MsalAccessTokenCacheKey_IsDifferentWhenEnvAndScopesAreDifferent()
+ public void MsalFamilyRefreshTokenCacheKey()
{
- MsalAccessTokenCacheKey key1 = new MsalAccessTokenCacheKey("env", "tid", "uid", "cid", "scope1 scope2");
- MsalAccessTokenCacheKey key2 = new MsalAccessTokenCacheKey("env", "tid", "uid", "cid",
- string.Join(" ", Enumerable.Range(1, 100).Select(i => "scope" + i)));
+ var key = new MsalRefreshTokenCacheKey("login.microsoftonline.com", "CLIENT_ID_NOT_USED", "uid.utid", "1");
- var serviceBundle = TestCommon.CreateDefaultServiceBundle();
- var crypto = serviceBundle.PlatformProxy.CryptographyManager;
-
- Assert.AreNotEqual(key1.GetUWPFixedSizeKey(crypto), key2.GetUWPFixedSizeKey(crypto));
- Assert.IsTrue(key2.GetUWPFixedSizeKey(crypto).Length < 255);
- Assert.IsTrue(key1.GetUWPFixedSizeKey(crypto).Length < 255);
+ Assert.AreEqual("uid.utid-login.microsoftonline.com-refreshtoken-1--", key.ToString());
+ Assert.AreEqual("uid.utid-login.microsoftonline.com", key.iOSAccount);
+ Assert.AreEqual("refreshtoken-1--", key.iOSService);
+ Assert.AreEqual("refreshtoken-1-", key.iOSGeneric);
+ Assert.AreEqual((int)MsalCacheKeys.iOSCredentialAttrType.RefreshToken, key.iOSType);
}
[TestMethod]
public void MsalIdTokenCacheKey()
{
- MsalIdTokenCacheKey key = new MsalIdTokenCacheKey("login.microsoftonline.com", "contoso.com", "uid.utid", "clientid");
+ var key = new MsalIdTokenCacheKey("login.microsoftonline.com", "contoso.com", "uid.utid", "clientid");
Assert.AreEqual("uid.utid-login.microsoftonline.com-idtoken-clientid-contoso.com-", key.ToString());
- Assert.AreEqual("uid.utid-login.microsoftonline.com", key.GetiOSAccountKey());
- Assert.AreEqual("idtoken-clientid-contoso.com-", key.GetiOSServiceKey());
- Assert.AreEqual("idtoken-clientid-contoso.com", key.GetiOSGenericKey());
+ Assert.AreEqual("uid.utid-login.microsoftonline.com", key.iOSAccount);
+ Assert.AreEqual("idtoken-clientid-contoso.com-", key.iOSService);
+ Assert.AreEqual("idtoken-clientid-contoso.com", key.iOSGeneric);
+ Assert.AreEqual((int)MsalCacheKeys.iOSCredentialAttrType.IdToken, key.iOSType);
}
[TestMethod]
public void MsalAccountCacheKey()
{
- MsalAccountCacheKey key = new MsalAccountCacheKey("login.microsoftonline.com", "contoso.com", "uid.utid", "localId");
+ var key = new MsalAccountCacheKey(
+ "login.microsoftonline.com",
+ "contoso.com",
+ "uid.utid",
+ "localId",
+ "AAD");
Assert.AreEqual("uid.utid-login.microsoftonline.com-contoso.com", key.ToString());
- Assert.AreEqual("uid.utid-login.microsoftonline.com", key.GetiOSAccountKey());
- Assert.AreEqual("contoso.com", key.GetiOSServiceKey());
- Assert.AreEqual("localid", key.GetiOSGenericKey());
+ Assert.AreEqual("uid.utid-login.microsoftonline.com", key.iOSAccount);
+ Assert.AreEqual("contoso.com", key.iOSService);
+ Assert.AreEqual("localid", key.iOSGeneric);
+ Assert.AreEqual(MsalCacheKeys.iOSAuthorityTypeToAttrType["AAD"], key.iOSType);
+
+ }
+
+ [TestMethod]
+ public void MsalAppMetadataCacheKey()
+ {
+ var key = new MsalAppMetadataCacheKey("clientid", "login.microsoftonline.com");
+
+ Assert.AreEqual("appmetadata-clientid", key.iOSService);
+ Assert.AreEqual("login.microsoftonline.com", key.iOSAccount);
+ Assert.AreEqual("1", key.iOSGeneric);
+ Assert.AreEqual((int)MsalCacheKeys.iOSCredentialAttrType.AppMetadata, key.iOSType);
}
}
}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/CoreTests/OAuth2Tests/TokenResponseTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/CoreTests/OAuth2Tests/TokenResponseTests.cs
index 3f5d013699..5e7b508246 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/CoreTests/OAuth2Tests/TokenResponseTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/CoreTests/OAuth2Tests/TokenResponseTests.cs
@@ -50,17 +50,8 @@ public void ExpirationTimeTest()
DateTimeOffset current = DateTimeOffset.UtcNow;
const long ExpiresInSeconds = 3599;
- MsalTokenResponse response = new MsalTokenResponse
- {
- IdToken =
- "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiI3YzdhMmY3MC1jYWVmLTQ1YzgtOWE2Yy0wOTE2MzM1MDFkZTQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vODE2OTAyODYtNTA1NC00Zjk3LWI3MDgtNTQxNjU0Y2Q5MjFhL3YyLjAvIiwiaWF0IjoxNDU1NTc2MjM1LCJuYmYiOjE0NTU1NzYyMzUsImV4cCI6MTQ1NTU4MDEzNSwibmFtZSI6IkFEQUwgT2JqLUMgLSBFMkUiLCJvaWQiOiIxZTcwYThlZi1jYjIwLTQxOWMtYjhhNy1hNDJlZDJmYTIyNzciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJlMmVAYWRhbG9iamMub25taWNyb3NvZnQuY29tIiwic3ViIjoibHJxVDlsQXQzSUlhS3hHanE2UlNReFRqN3diV3Q2RnpaMFU3NkJZMEJINCIsInRpZCI6IjgxNjkwMjg2LTUwNTQtNGY5Ny1iNzA4LTU0MTY1NGNkOTIxYSIsInZlciI6IjIuMCJ9.axS_-N3Z3b1GnZftxb6dKtMeooldoIQ_B7YrVO4CQI9xhHI1_Vl-dXfsFHBPRvIvXBEfBEehaaWq9B9P_CD5TpQXGycsYS08knHf_QpHIJ9WQbBIJ774divakx7kN6x7IxjoD1PrfRfo2QZsLLAz-1n-NHt7FwtkBQpKTDfgc6cVShy9isaJt5WoxfUM1eNo1HK_YjHj7Q5-n-XiZEbe-8m-7nqwBw86QDlLdk7dBhhCzVzXZb_5HCHI-23xZLYR34RoW7ljYEG4P8auEcML1haS4MN83VKRorMyljAIoA4YOgbfnvnlAlxRz_rtAAcjNqaUpIwzadGzd-QVbyoKPQ",
- AccessToken = "access-token",
- ExpiresIn = ExpiresInSeconds,
- CorrelationId = "correlation-id",
- RefreshToken = "refresh-token",
- Scope = "scope1 scope2",
- TokenType = "Bearer"
- };
+ var response = MsalTestConstants.CreateMsalTokenResponse();
+
Assert.IsTrue(response.AccessTokenExpiresOn.Subtract(current) >= TimeSpan.FromSeconds(ExpiresInSeconds));
}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/OAuthClientTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/OAuthClientTests.cs
new file mode 100644
index 0000000000..f9b6284d09
--- /dev/null
+++ b/tests/Microsoft.Identity.Test.Unit.net45/OAuthClientTests.cs
@@ -0,0 +1,239 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// ------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Identity.Client;
+using Microsoft.Identity.Client.ApiConfig.Parameters;
+using Microsoft.Identity.Client.Exceptions;
+using Microsoft.Identity.Client.Internal.Requests;
+using Microsoft.Identity.Test.Common;
+using Microsoft.Identity.Test.Common.Core.Mocks;
+using Microsoft.Identity.Test.Common.Mocks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using static Microsoft.Identity.Test.Unit.RequestsTests.InteractiveRequestTests;
+
+namespace Microsoft.Identity.Test.Unit
+{
+ [TestClass]
+ public class OAuthClientTests
+ {
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ TestCommon.ResetStateAndInitMsal();
+ }
+
+ [TestMethod]
+ public void RedirectUriContainsFragmentErrorTest()
+ {
+ try
+ {
+ using (var harness = new MockHttpAndServiceBundle())
+ {
+ AuthenticationRequestParameters parameters = harness.CreateAuthenticationRequestParameters(
+ MsalTestConstants.AuthorityHomeTenant,
+ MsalTestConstants.Scope,
+ null,
+ extraQueryParameters: new Dictionary
+ {
+ {"extra", "qp"}
+ });
+ parameters.RedirectUri = new Uri("some://uri#fragment=not-so-good");
+ parameters.LoginHint = MsalTestConstants.DisplayableId;
+ var interactiveParameters = new AcquireTokenInteractiveParameters
+ {
+ Prompt = Prompt.ForceLogin,
+ ExtraScopesToConsent = MsalTestConstants.ScopeForAnotherResource.ToArray(),
+ };
+
+ new InteractiveRequest(
+ harness.ServiceBundle,
+ parameters,
+ interactiveParameters,
+ new MockWebUI());
+
+ Assert.Fail("ArgumentException should be thrown here");
+ }
+ }
+ catch (ArgumentException ae)
+ {
+ Assert.IsTrue(ae.Message.Contains(MsalErrorMessage.RedirectUriContainsFragment));
+ }
+ }
+
+ [TestMethod]
+ public void OAuthClient_FailsWithServiceExceptionWhenItCannotParseJsonResponse()
+ {
+ ValidateOathClient(
+ MockHelpers.CreateTooManyRequestsNonJsonResponse(),
+ exception =>
+ {
+ MsalServiceException serverEx = exception.InnerException as MsalServiceException;
+ Assert.IsNotNull(serverEx);
+ Assert.AreEqual(429, serverEx.StatusCode);
+ Assert.AreEqual(MockHelpers.TooManyRequestsContent, serverEx.ResponseBody);
+ Assert.AreEqual(MockHelpers.TestRetryAfterDuration, serverEx.Headers.RetryAfter.Delta);
+ Assert.AreEqual(MsalError.NonParsableOAuthError, serverEx.ErrorCode);
+ });
+ }
+
+ [TestMethod]
+ public void OAuthClient_FailsWithServiceExceptionWhenItCanParseJsonResponse()
+ {
+ ValidateOathClient(
+ MockHelpers.CreateTooManyRequestsJsonResponse(),
+ exception =>
+ {
+ MsalServiceException serverEx = exception.InnerException as MsalServiceException;
+ Assert.IsNotNull(serverEx);
+ Assert.AreEqual(429, serverEx.StatusCode);
+ Assert.AreEqual(MockHelpers.TestRetryAfterDuration, serverEx.Headers.RetryAfter.Delta);
+ Assert.AreEqual("Server overload", serverEx.ErrorCode);
+ });
+ }
+
+ [TestMethod]
+ public void OAuthClient_FailsWithServiceExceptionWhenEntireResponseIsNull()
+ {
+ ValidateOathClient(
+ null,
+ exception =>
+ {
+ var innerException = exception.InnerException as InvalidOperationException;
+ Assert.IsNotNull(innerException);
+ });
+ }
+
+ [TestMethod]
+ public void OAuthClient_FailsWithServiceExceptionWhenResponseIsEmpty()
+ {
+ ValidateOathClient(
+ MockHelpers.CreateEmptyResponseMessage(),
+ exception =>
+ {
+ var serverEx = exception.InnerException as MsalServiceException;
+ Assert.IsNotNull(serverEx);
+ Assert.AreEqual((int)HttpStatusCode.BadRequest, serverEx.StatusCode);
+ Assert.IsNotNull(serverEx.ResponseBody);
+ Assert.AreEqual(MsalError.HttpStatusCodeNotOk, serverEx.ErrorCode);
+ });
+ }
+
+ [TestMethod]
+ public void OAuthClient_FailsWithServiceExceptionWhenResponseIsNull()
+ {
+ ValidateOathClient(
+ MockHelpers.CreateNullResponseMessage(),
+ exception =>
+ {
+ var serverEx = exception.InnerException as MsalServiceException;
+ Assert.IsNotNull(serverEx);
+ Assert.AreEqual((int)HttpStatusCode.BadRequest, serverEx.StatusCode);
+ Assert.IsNull(serverEx.ResponseBody);
+ Assert.AreEqual(MsalError.HttpStatusCodeNotOk, serverEx.ErrorCode);
+ });
+ }
+
+ [TestMethod]
+ public void OAuthClient_FailsWithServiceExceptionWhenResponseDoesNotContainAnErrorField()
+ {
+ ValidateOathClient(
+ MockHelpers.CreateNoErrorFieldResponseMessage(),
+ exception =>
+ {
+ var serverEx = exception.InnerException as MsalServiceException;
+ Assert.IsNotNull(serverEx);
+ Assert.AreEqual((int)HttpStatusCode.BadRequest, serverEx.StatusCode);
+ Assert.IsNotNull(serverEx.ResponseBody);
+ Assert.AreEqual(MsalError.HttpStatusCodeNotOk, serverEx.ErrorCode);
+ });
+ }
+
+ [TestMethod]
+ public void OAuthClient_FailsWithServiceExceptionWhenResponseIsHttpNotFound()
+ {
+ ValidateOathClient(
+ MockHelpers.CreateHttpStatusNotFoundResponseMessage(),
+ exception =>
+ {
+ var serverEx = exception.InnerException as MsalServiceException;
+ Assert.IsNotNull(serverEx);
+ Assert.AreEqual((int)HttpStatusCode.NotFound, serverEx.StatusCode);
+ Assert.IsNotNull(serverEx.ResponseBody);
+ Assert.AreEqual(MsalError.HttpStatusNotFound, serverEx.ErrorCode);
+ });
+ }
+
+ private static void ValidateOathClient(HttpResponseMessage httpResponseMessage, Action validationHandler)
+ {
+ using (MockHttpAndServiceBundle harness = new MockHttpAndServiceBundle())
+ {
+ harness.HttpManager.AddMockHandler(
+ new MockHttpMessageHandler
+ {
+ ExpectedMethod = HttpMethod.Get,
+ ResponseMessage = httpResponseMessage
+ });
+
+ AuthenticationRequestParameters parameters = harness.CreateAuthenticationRequestParameters(
+ MsalTestConstants.AuthorityHomeTenant,
+ MsalTestConstants.Scope,
+ null);
+ parameters.RedirectUri = new Uri("some://uri");
+ parameters.LoginHint = MsalTestConstants.DisplayableId;
+ AcquireTokenInteractiveParameters interactiveParameters = new AcquireTokenInteractiveParameters
+ {
+ Prompt = Prompt.SelectAccount,
+ ExtraScopesToConsent = MsalTestConstants.ScopeForAnotherResource.ToArray(),
+ };
+
+ InteractiveRequest request = new InteractiveRequest(
+ harness.ServiceBundle,
+ parameters,
+ interactiveParameters,
+ new MockWebUI());
+
+ try
+ {
+ request.ExecuteAsync(CancellationToken.None).Wait();
+ Assert.Fail("MsalException should have been thrown here");
+ }
+ catch (Exception exc)
+ {
+ validationHandler(exc);
+ }
+ }
+ }
+
+ }
+}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/AcquireTokenSilentTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/AcquireTokenSilentTests.cs
index 66e6ad1e40..5be37a5301 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/AcquireTokenSilentTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/AcquireTokenSilentTests.cs
@@ -138,7 +138,7 @@ public void AcquireTokenSilentScopeAndUserOverloadWithNoMatchingScopesInCacheTes
Assert.IsNotNull(result);
Assert.AreEqual(MsalTestConstants.DisplayableId, result.Account.Username);
Assert.AreEqual(MsalTestConstants.ScopeForAnotherResource.AsSingleString(), result.Scopes.AsSingleString());
- Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -248,9 +248,8 @@ public void AcquireTokenSilentCacheOnlyLookupTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.AsSingleString(), result.Scopes.AsSingleString());
- Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
-
+ Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
Assert.IsNotNull(receiver.EventsReceived.Find(anEvent => // Expect finding such an event
anEvent[EventBase.EventNameKey].EndsWith("api_event") && anEvent[ApiEvent.WasSuccessfulKey] == "true"
&& anEvent[ApiEvent.ApiIdKey] == "31"));
@@ -369,8 +368,8 @@ public void AcquireTokenSilentForceRefreshTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.ToArray().AsSingleString(), result.Scopes.AsSingleString());
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
}
}
@@ -413,9 +412,8 @@ public void AcquireTokenSilentForceRefreshMultipleTenantsTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.ToArray().AsSingleString(), result.Scopes.AsSingleString());
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
-
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
httpManager.AddMockHandlerForTenantEndpointDiscovery(MsalTestConstants.AuthorityGuidTenant2);
httpManager.AddMockHandler(
@@ -441,8 +439,8 @@ public void AcquireTokenSilentForceRefreshMultipleTenantsTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result2.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.ToArray().AsSingleString(), result2.Scopes.AsSingleString());
- Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
httpManager.AddMockHandlerForTenantEndpointDiscovery(MsalTestConstants.AuthorityGuidTenant);
@@ -469,8 +467,8 @@ public void AcquireTokenSilentForceRefreshMultipleTenantsTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result3.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.ToArray().AsSingleString(), result3.Scopes.AsSingleString());
- Assert.AreEqual(3, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(3, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
// Use same authority as above, number of access tokens should remain constant
httpManager.AddMockHandler(
@@ -494,8 +492,8 @@ public void AcquireTokenSilentForceRefreshMultipleTenantsTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result4.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.ToArray().AsSingleString(), result4.Scopes.AsSingleString());
- Assert.AreEqual(3, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(3, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
}
}
@@ -538,8 +536,8 @@ public void AcquireTokenSilentForceRefreshFalseMultipleTenantsTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.ToArray().AsSingleString(), result.Scopes.AsSingleString());
- Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
httpManager.AddMockHandlerForTenantEndpointDiscovery(MsalTestConstants.AuthorityGuidTenant2);
@@ -566,8 +564,8 @@ public void AcquireTokenSilentForceRefreshFalseMultipleTenantsTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result2.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.ToArray().AsSingleString(), result2.Scopes.AsSingleString());
- Assert.AreEqual(3, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(3, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
httpManager.AddMockHandlerForTenantEndpointDiscovery(MsalTestConstants.AuthorityGuidTenant);
@@ -594,8 +592,8 @@ public void AcquireTokenSilentForceRefreshFalseMultipleTenantsTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result3.Account.Username);
Assert.AreEqual(MsalTestConstants.Scope.ToArray().AsSingleString(), result3.Scopes.AsSingleString());
- Assert.AreEqual(4, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(4, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
}
}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/ConfidentialClientApplicationTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/ConfidentialClientApplicationTests.cs
index 1c58e393a1..3a7fff45bb 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/ConfidentialClientApplicationTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/ConfidentialClientApplicationTests.cs
@@ -251,12 +251,12 @@ public async Task ConfidentialClientUsingSecretTestAsync()
Assert.AreEqual(MsalTestConstants.Scope.AsSingleString(), result.Scopes.AsSingleString());
// make sure user token cache is empty
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
// check app token cache count to be 1
- Assert.AreEqual(1, app.AppTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(0, app.AppTokenCacheInternal.Accessor.RefreshTokenCount); // no refresh tokens are returned
+ Assert.AreEqual(1, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, app.AppTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
// call AcquireTokenForClientAsync again to get result back from the cache
result = await app.AcquireTokenForClientAsync(MsalTestConstants.Scope.ToArray()).ConfigureAwait(false);
@@ -265,12 +265,12 @@ public async Task ConfidentialClientUsingSecretTestAsync()
Assert.AreEqual(MsalTestConstants.Scope.AsSingleString(), result.Scopes.AsSingleString());
// make sure user token cache is empty
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
// check app token cache count to be 1
- Assert.AreEqual(1, app.AppTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(0, app.AppTokenCacheInternal.Accessor.RefreshTokenCount); // no refresh tokens are returned
+ Assert.AreEqual(1, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, app.AppTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
}
}
@@ -317,12 +317,12 @@ public async Task ConfidentialClientUsingCertificateTestAsync()
Assert.AreEqual(MsalTestConstants.Scope.AsSingleString(), result.Scopes.AsSingleString());
// make sure user token cache is empty
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
// check app token cache count to be 1
- Assert.AreEqual(1, app.AppTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(0, app.AppTokenCacheInternal.Accessor.RefreshTokenCount); // no refresh tokens are returned
+ Assert.AreEqual(1, app.AppTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, app.AppTokenCacheInternal.Accessor.GetAllRefreshTokens().Count()); // no RTs are returned
// assert client credential
Assert.IsNotNull(cc.Assertion);
@@ -671,8 +671,8 @@ public async Task AuthorizationCodeRequestTestAsync()
var result = await app.AcquireTokenByAuthorizationCodeAsync("some-code", MsalTestConstants.Scope)
.ConfigureAwait(false);
Assert.IsNotNull(result);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
app = ConfidentialClientApplicationBuilder.Create(MsalTestConstants.ClientId)
.WithAuthority(new Uri("https://" + MsalTestConstants.ProductionPrefNetworkEnvironment + "/tfp/home/policy"), true)
@@ -708,16 +708,16 @@ public async Task AcquireTokenByRefreshTokenTestAsync()
var result = await (app as IByRefreshToken).AcquireTokenByRefreshTokenAsync(null, "SomeRefreshToken").ConfigureAwait(false);
- Assert.AreEqual(app.UserTokenCacheInternal.Accessor.RefreshTokenCount, 1);
- Assert.AreEqual(app.UserTokenCacheInternal.Accessor.AccessTokenCount, 1);
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
Assert.IsNotNull(result.AccessToken);
Assert.AreEqual(result.AccessToken, "some-access-token");
app.UserTokenCacheInternal.Clear();
httpManager.AddSuccessTokenResponseMockHandlerForPost(MsalTestConstants.AuthorityCommonTenant);
result = await ((IByRefreshToken)app).AcquireTokenByRefreshTokenAsync(MsalTestConstants.Scope, "SomeRefreshToken").ConfigureAwait(false);
- Assert.AreEqual(app.UserTokenCacheInternal.Accessor.RefreshTokenCount, 1);
- Assert.AreEqual(app.UserTokenCacheInternal.Accessor.AccessTokenCount, 1);
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
Assert.IsNotNull(result.AccessToken);
Assert.AreEqual(result.AccessToken, "some-access-token");
}
@@ -780,4 +780,4 @@ private void AfterCacheAccess(TokenCacheNotificationArgs args)
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/PublicClientApplicationTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/PublicClientApplicationTests.cs
index e717216ce0..8c1f87fc74 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/PublicClientApplicationTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/PublicClientApplicationTests.cs
@@ -283,15 +283,7 @@ public void AcquireTokenSameUserTest()
PublicClientApplication app = PublicClientApplicationBuilder.Create(MsalTestConstants.ClientId)
.WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true)
.WithHttpManager(harness.HttpManager)
- .BuildConcrete();
-
- MockWebUI ui = new MockWebUI()
- {
- MockResult = new AuthorizationResult(
- AuthorizationStatus.Success,
- MsalTestConstants.AuthorityHomeTenant + "?code=some-code")
- };
-
+ .BuildConcrete();
MsalMockHelpers.ConfigureMockWebUI(
app.ServiceBundle.PlatformProxy,
new AuthorizationResult(AuthorizationStatus.Success, app.AppConfig.RedirectUri + "?code=some-code"));
@@ -454,7 +446,7 @@ public void AcquireTokenDifferentUserReturnedFromServiceTest()
var users = app.GetAccountsAsync().Result;
Assert.AreEqual(1, users.Count());
- Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -511,7 +503,7 @@ public void AcquireTokenNullUserPassedInAndNewUserReturnedFromServiceTest()
Assert.AreEqual(MsalTestConstants.DisplayableId, result.Account.Username);
var users = app.GetAccountsAsync().Result;
Assert.AreEqual(2, users.Count());
- Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -578,7 +570,7 @@ public void GetUsersTest()
app.UserTokenCacheInternal.Accessor.SaveAccount(accountCacheItem);
- Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
users = app.GetAccountsAsync().Result;
Assert.IsNotNull(users);
Assert.AreEqual(2, users.Count());
@@ -591,13 +583,61 @@ public void GetUsersTest()
MockHelpers.CreateClientInfo(MsalTestConstants.Uid + "more1", MsalTestConstants.Utid));
app.UserTokenCacheInternal.Accessor.SaveRefreshToken(rtItem);
- Assert.AreEqual(3, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(3, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
users = app.GetAccountsAsync().Result;
Assert.IsNotNull(users);
Assert.AreEqual(2, users.Count());
}
}
+ [TestMethod]
+ public async Task TestAccountAcrossMultipleClientIdsAsync()
+ {
+ // Arrange
+
+ PublicClientApplication app = PublicClientApplicationBuilder.Create(MsalTestConstants.ClientId).BuildConcrete();
+
+ // Populate with tokens tied to ClientId2
+ _tokenCacheHelper.PopulateCache(app.UserTokenCacheInternal.Accessor, clientId: MsalTestConstants.ClientId2);
+
+ app.UserTokenCacheInternal.Accessor.AssertItemCount(
+ expectedAtCount: 2,
+ expectedRtCount: 1,
+ expectedAccountCount: 1,
+ expectedIdtCount: 1,
+ expectedAppMetadataCount: 1);
+
+ // Act
+ var accounts = await app.GetAccountsAsync().ConfigureAwait(false);
+
+ // Assert - an account is returned even if app is scoped to ClientId1
+ Assert.AreEqual(1, accounts.Count());
+
+ // Arrange
+
+ // Populate for clientid2
+ _tokenCacheHelper.PopulateCache(app.UserTokenCacheInternal.Accessor, clientId: MsalTestConstants.ClientId);
+
+ app.UserTokenCacheInternal.Accessor.AssertItemCount(
+ expectedAtCount: 4,
+ expectedRtCount: 2,
+ expectedAccountCount: 1, // still 1 account
+ expectedIdtCount: 2,
+ expectedAppMetadataCount: 2);
+
+ // Act
+ await app.RemoveAsync(accounts.Single()).ConfigureAwait(false);
+
+ // Assert
+ app.UserTokenCacheInternal.Accessor.AssertItemCount(
+ expectedAtCount: 0,
+ expectedRtCount: 0,
+ expectedAccountCount: 0,
+ expectedIdtCount: 0,
+ expectedAppMetadataCount: 2); // app metadata is never deleted
+
+ }
+
[TestMethod]
[TestCategory("PublicClientApplicationTests")]
public void GetUsersAndSignThemOutTest()
@@ -610,8 +650,8 @@ public void GetUsersAndSignThemOutTest()
app.RemoveAsync(user).Wait();
}
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.RefreshTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count());
}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/BrokerRequestTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/BrokerRequestTests.cs
index 8dd902499a..6867c42e18 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/BrokerRequestTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/BrokerRequestTests.cs
@@ -131,7 +131,7 @@ public void BrokerUnknownErrorResponseTest()
}
- private void ValidateBrokerResponse(MsalTokenResponse msalTokenResponse, OAuthClientValidationHandler validationHandler)
+ private void ValidateBrokerResponse(MsalTokenResponse msalTokenResponse, Action validationHandler)
{
try
{
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/DeviceCodeRequestTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/DeviceCodeRequestTests.cs
index e07e16653d..9b7be4b027 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/DeviceCodeRequestTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/DeviceCodeRequestTests.cs
@@ -100,10 +100,10 @@ public void TestDeviceCodeAuthSuccess()
var cache = parameters.CacheSessionManager.TokenCacheInternal;
// Check that cache is empty
- Assert.AreEqual(0, cache.Accessor.AccessTokenCount);
- Assert.AreEqual(0, cache.Accessor.AccountCount);
- Assert.AreEqual(0, cache.Accessor.IdTokenCount);
- Assert.AreEqual(0, cache.Accessor.RefreshTokenCount);
+ Assert.AreEqual(0, cache.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(0, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(0, cache.Accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(0, cache.Accessor.GetAllAccounts().Count());
DeviceCodeResult actualDeviceCodeResult = null;
@@ -135,10 +135,10 @@ public void TestDeviceCodeAuthSuccess()
CoreAssert.AreScopesEqual(expectedScopes.AsSingleString(), actualDeviceCodeResult.Scopes.AsSingleString());
// Validate that entries were added to cache
- Assert.AreEqual(1, cache.Accessor.AccessTokenCount);
- Assert.AreEqual(1, cache.Accessor.AccountCount);
- Assert.AreEqual(1, cache.Accessor.IdTokenCount);
- Assert.AreEqual(1, cache.Accessor.RefreshTokenCount);
+ Assert.AreEqual(1, cache.Accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(1, cache.Accessor.GetAllAccounts().Count());
}
}
@@ -333,4 +333,4 @@ private class _LogData
public bool IsPii { get; set; }
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/FociTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/FociTests.cs
new file mode 100644
index 0000000000..e6846c2aff
--- /dev/null
+++ b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/FociTests.cs
@@ -0,0 +1,320 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// ------------------------------------------------------------------------------
+
+using System;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Identity.Client;
+using Microsoft.Identity.Client.AppConfig;
+using Microsoft.Identity.Client.UI;
+using Microsoft.Identity.Client.Utils;
+using Microsoft.Identity.Test.Common;
+using Microsoft.Identity.Test.Common.Core.Helpers;
+using Microsoft.Identity.Test.Common.Core.Mocks;
+using Microsoft.Identity.Test.Common.Mocks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.Identity.Test.Unit.RequestsTests
+{
+ [TestClass]
+ public class FociTests
+ {
+ private enum ServerTokenResponse
+ {
+ NonFociToken,
+ FociToken,
+ Error
+ }
+
+ private string _inMemoryCache;
+ private PublicClientApplication _appA;
+ private PublicClientApplication _appB;
+ private MockHttpAndServiceBundle _harness;
+ private bool _instanceAndEndpointRequestPerformed = false;
+
+ [TestInitialize]
+ public void Init()
+ {
+ TestCommon.ResetStateAndInitMsal();
+ _inMemoryCache = "{}";
+ _instanceAndEndpointRequestPerformed = false;
+ }
+
+ ///
+ /// A and B apps part of the family. A acquires a token interactively. B can now acquire a token silently.
+ ///
+ [TestMethod]
+ public async Task FociHappyPathAsync()
+ {
+ // Arrange
+ using (_harness = new MockHttpAndServiceBundle())
+ {
+ InitApps();
+
+ // Act
+ await InteractiveAsync(_appA, ServerTokenResponse.FociToken).ConfigureAwait(false);
+ await SilentAsync(_appB, ServerTokenResponse.FociToken).ConfigureAwait(false);
+
+ // Assert
+ await AssertAccountsAsync().ConfigureAwait(false);
+
+ // Make sure smth reloads the cache before using the Accessor from the other app (GetAccounts will)
+ Assert.AreEqual(2, _appA.UserTokenCacheInternal.Accessor.GetAllAppMetadata().Count());
+ Assert.IsTrue(_appA.UserTokenCacheInternal.Accessor.GetAllAppMetadata().All(am => am.FamilyId == "1"));
+ Assert.AreEqual("1", _appA.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Single().FamilyId);
+ }
+ }
+
+ ///
+ /// A is part of the family, B is not. B fails gracefully trying to get a token silently
+ ///
+ [TestMethod]
+ public async Task FociAndNonFociAppsCoexistAsync()
+ {
+ using (_harness = new MockHttpAndServiceBundle())
+ {
+ InitApps();
+
+ // Act
+ await InteractiveAsync(_appA, ServerTokenResponse.FociToken).ConfigureAwait(false);
+
+ // B cannot acquire a token interactivelty, but will try to use FRT
+ AssertException.TaskThrows(() => SilentAsync(_appB, ServerTokenResponse.Error));
+
+ // B can resume acquiring tokens silently via the normal RT, after a non
+ await InteractiveAsync(_appB, ServerTokenResponse.NonFociToken).ConfigureAwait(false);
+ await SilentAsync(_appB, ServerTokenResponse.NonFociToken).ConfigureAwait(false);
+
+ // Assert
+ await AssertAccountsAsync().ConfigureAwait(false);
+ AssertAppHasRT(_appB);
+ AssertFRTExists();
+
+ }
+ }
+
+ ///
+ /// B is not part of the family but has an RT. B joins the family. B starts using the FRT.
+ ///
+ [TestMethod]
+ public async Task FociAppWithTokensJoinsFamilyAsync()
+ {
+ using (_harness = new MockHttpAndServiceBundle())
+ {
+ InitApps();
+
+ // A is in the family and has brought down an FRT. B has brought down it's RT.
+ await InteractiveAsync(_appA, ServerTokenResponse.FociToken).ConfigureAwait(false);
+ await InteractiveAsync(_appB, ServerTokenResponse.NonFociToken).ConfigureAwait(false);
+
+ // B refreshes it's RT and gets an FRT response
+ await SilentAsync(_appB, ServerTokenResponse.FociToken).ConfigureAwait(false);
+
+ // remove B's RT
+ var art = _appB.UserTokenCacheInternal.Accessor.GetAllRefreshTokens()
+ .Single(rt => _appB.ClientId == rt.ClientId && string.IsNullOrEmpty(rt.FamilyId));
+ _appB.UserTokenCacheInternal.Accessor.DeleteRefreshToken(art.GetKey());
+
+ // B can still use the FRT
+ await SilentAsync(_appB, ServerTokenResponse.FociToken).ConfigureAwait(false);
+
+ // Assert
+ await AssertAccountsAsync().ConfigureAwait(false);
+ AssertFRTExists();
+ }
+ }
+
+ ///
+ /// A and B apps part of the family. B leaves the family. B fails gracefully trying to get a token silently
+ ///
+ [TestMethod]
+ public async Task FociAppLeavesFamilyAsync()
+ {
+ using (_harness = new MockHttpAndServiceBundle())
+ {
+ InitApps();
+
+ // A and B are part of the family
+ await InteractiveAsync(_appA, ServerTokenResponse.FociToken).ConfigureAwait(false);
+ await SilentAsync(_appB, ServerTokenResponse.FociToken).ConfigureAwait(false);
+
+ // B leaves the family -> STS will not refresh its token based on the FRT
+ AssertException.TaskThrows(() => SilentAsync(_appB, ServerTokenResponse.Error));
+
+ // B can resume acquiring tokens silently via the normal RT, after an interactive flow
+ await InteractiveAsync(_appB, ServerTokenResponse.NonFociToken).ConfigureAwait(false);
+ await SilentAsync(_appB, ServerTokenResponse.NonFociToken).ConfigureAwait(false);
+
+ // Assert
+ await AssertAccountsAsync().ConfigureAwait(false);
+
+ AssertAppHasRT(_appB);
+ AssertFRTExists();
+ }
+ }
+
+
+ private void AssertAppMetadata(PublicClientApplication app, bool partOfFamily)
+ {
+ Assert.IsNotNull(
+ app.UserTokenCacheInternal.Accessor.GetAllAppMetadata()
+ .Single(m => m.ClientId == app.ClientId &&
+ partOfFamily == !string.IsNullOrEmpty(m.FamilyId)));
+ }
+
+ private async Task SilentAsync(
+ PublicClientApplication app,
+ ServerTokenResponse serverTokenResponse)
+ {
+ var account = (await app.GetAccountsAsync().ConfigureAwait(false)).Single();
+
+ // 2 network calls - one for endpoint discovery on the tenanted authority, one to refresh the token
+ if (!_instanceAndEndpointRequestPerformed)
+ {
+ _instanceAndEndpointRequestPerformed = true;
+ _harness.HttpManager.AddInstanceDiscoveryMockHandler();
+
+ _harness.HttpManager.AddMockHandlerForTenantEndpointDiscovery(MsalTestConstants.AuthorityUtidTenant);
+ }
+
+ _harness.HttpManager.AddMockHandler(
+ new MockHttpMessageHandler()
+ {
+ ExpectedMethod = HttpMethod.Post,
+ ResponseMessage =
+ (serverTokenResponse == ServerTokenResponse.Error) ?
+ MockHelpers.CreateInvalidGrantTokenResponseMessage() :
+ MockHelpers.CreateSuccessTokenResponseMessage(
+ MsalTestConstants.UniqueId,
+ MsalTestConstants.DisplayableId,
+ MsalTestConstants.Scope.ToArray(),
+ foci: serverTokenResponse == ServerTokenResponse.FociToken)
+ });
+
+ AuthenticationResult resultB = await app.AcquireTokenSilent(MsalTestConstants.Scope, account)
+ .WithForceRefresh(true)
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.IsNotNull(resultB.AccessToken);
+ AssertAppMetadata(app, serverTokenResponse == ServerTokenResponse.FociToken);
+
+ }
+
+ private async Task InteractiveAsync(PublicClientApplication app, ServerTokenResponse serverTokenResponse)
+ {
+ if (serverTokenResponse == ServerTokenResponse.Error)
+ {
+ throw new NotImplementedException("test error");
+ }
+
+ if (!_instanceAndEndpointRequestPerformed)
+ {
+ _instanceAndEndpointRequestPerformed = true;
+
+ _harness.HttpManager.AddInstanceDiscoveryMockHandler();
+ _harness.HttpManager.AddMockHandlerForTenantEndpointDiscovery(MsalTestConstants.AuthorityUtidTenant);
+ }
+
+ MsalMockHelpers.ConfigureMockWebUI(
+ app.ServiceBundle.PlatformProxy,
+ new AuthorizationResult(AuthorizationStatus.Success, app.AppConfig.RedirectUri + "?code=some-code"));
+
+ _harness.HttpManager.AddSuccessTokenResponseMockHandlerForPost(
+ MsalTestConstants.AuthorityUtidTenant,
+ foci: serverTokenResponse == ServerTokenResponse.FociToken);
+
+ // Acquire token interactively for A
+ AuthenticationResult result = await app.AcquireTokenAsync(MsalTestConstants.Scope).ConfigureAwait(false);
+ Assert.IsNotNull(result.Account);
+ AssertAppMetadata(app, serverTokenResponse == ServerTokenResponse.FociToken);
+
+ }
+
+ private void InitApps()
+ {
+
+ _appA = PublicClientApplicationBuilder.Create(MsalTestConstants.ClientId)
+ .WithHttpManager(_harness.HttpManager)
+ .WithAuthority(MsalTestConstants.AuthorityUtidTenant)
+ .BuildConcrete();
+
+ _appB = PublicClientApplicationBuilder.Create(MsalTestConstants.ClientId2)
+ .WithHttpManager(_harness.HttpManager)
+ .WithAuthority(MsalTestConstants.AuthorityUtidTenant)
+ .BuildConcrete();
+ ConfigureCacheSerialization(_appA);
+ ConfigureCacheSerialization(_appB);
+ }
+
+
+ private void ConfigureCacheSerialization(IPublicClientApplication pca)
+ {
+ pca.UserTokenCache.SetBeforeAccess(notificationArgs =>
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(_inMemoryCache);
+ notificationArgs.TokenCache.DeserializeMsalV3(bytes);
+ });
+
+ pca.UserTokenCache.SetAfterAccess(notificationArgs =>
+ {
+ if (notificationArgs.HasStateChanged)
+ {
+ byte[] bytes = notificationArgs.TokenCache.SerializeMsalV3();
+ _inMemoryCache = Encoding.UTF8.GetString(bytes);
+ }
+ });
+ }
+
+
+ private async Task AssertAccountsAsync()
+ {
+ Assert.AreEqual(1, (await _appA.GetAccountsAsync().ConfigureAwait(false)).Count());
+ Assert.AreEqual(1, (await _appB.GetAccountsAsync().ConfigureAwait(false)).Count());
+ }
+
+
+ private void AssertFRTExists()
+ {
+ Assert.IsTrue(_appA.UserTokenCacheInternal.Accessor.GetAllRefreshTokens()
+ .Any(rt => !string.IsNullOrEmpty(rt.FamilyId)),
+ "The FRT still exists");
+ }
+
+ private void AssertAppHasRT(PublicClientApplication app)
+ {
+ Assert.IsTrue(app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens()
+ .Any(rt => rt.ClientId == _appB.ClientId && string.IsNullOrEmpty(rt.FamilyId)),
+ "App B has a normal RT associated");
+
+ }
+
+
+ }
+}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/IntegratedWindowsAuthUsernamePasswordTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/IntegratedWindowsAuthUsernamePasswordTests.cs
index 771aa60353..1d5bc08101 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/IntegratedWindowsAuthUsernamePasswordTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/IntegratedWindowsAuthUsernamePasswordTests.cs
@@ -395,7 +395,7 @@ public void MexEndpointFailsToResolveTest()
Assert.AreEqual("parsing_ws_metadata_exchange_failed", result.ErrorCode);
// There should be no cached entries.
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -431,7 +431,7 @@ public void MexDoesNotReturnAuthEndpointTest()
Assert.AreEqual(MsalError.ParsingWsTrustResponseFailed, result.ErrorCode);
// There should be no cached entries.
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -465,7 +465,7 @@ public void MexParsingFailsTest()
Assert.AreEqual("Response status code does not indicate success: 404 (NotFound).", result.Message);
// There should be no cached entries.
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -504,7 +504,7 @@ public void FederatedUsernameNullPasswordTest()
Assert.AreEqual(MsalError.ParsingWsTrustResponseFailed, result.ErrorCode);
// There should be no cached entries.
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -547,7 +547,7 @@ public void FederatedUsernamePasswordCommonAuthorityTest()
Assert.AreEqual(MsalError.InvalidRequest, result.ErrorCode);
// There should be no cached entries.
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -599,7 +599,7 @@ public void ManagedUsernamePasswordCommonAuthorityTest()
Assert.AreEqual(MsalError.InvalidRequest, result.ErrorCode);
// There should be no cached entries.
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -669,7 +669,7 @@ public void ManagedUsernameNoPasswordAcquireTokenTest()
Assert.AreEqual(MsalError.PasswordRequiredForManagedUserError, result.ErrorCode);
// There should be no cached entries.
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
@@ -715,7 +715,7 @@ public void ManagedUsernameIncorrectPasswordAcquireTokenTest()
Assert.AreEqual(MsalError.InvalidGrantError, result.ErrorCode);
// There should be no cached entries.
- Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.AccessTokenCount);
+ Assert.AreEqual(0, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count());
}
}
#endif
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/InteractiveRequestTests.cs b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/InteractiveRequestTests.cs
index ab83cb7e45..278dfa534c 100644
--- a/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/InteractiveRequestTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit.net45/RequestsTests/InteractiveRequestTests.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
// All rights reserved.
@@ -25,8 +25,15 @@
//
// ------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.ApiConfig.Parameters;
+using Microsoft.Identity.Client.Cache.Items;
using Microsoft.Identity.Client.Exceptions;
using Microsoft.Identity.Client.Internal.Requests;
using Microsoft.Identity.Client.OAuth2;
@@ -38,21 +45,12 @@
using Microsoft.Identity.Test.Common.Mocks;
using Microsoft.Identity.Test.Unit.PublicApiTests;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Identity.Client.Cache.Items;
-using Microsoft.Identity.Client.Internal.Broker;
namespace Microsoft.Identity.Test.Unit.RequestsTests
{
[TestClass]
public class InteractiveRequestTests
- {
+ {
[TestInitialize]
public void TestInitialize()
{
@@ -115,8 +113,8 @@ public async Task WithExtraQueryParamsAndClaimsAsync()
AuthenticationResult result = await request.RunAsync(CancellationToken.None).ConfigureAwait(false);
Assert.IsNotNull(result);
- Assert.AreEqual(1, ((ITokenCacheInternal)cache).Accessor.RefreshTokenCount);
- Assert.AreEqual(1, ((ITokenCacheInternal)cache).Accessor.AccessTokenCount);
+ Assert.AreEqual(1, ((ITokenCacheInternal)cache).Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(1, ((ITokenCacheInternal)cache).Accessor.GetAllAccessTokens().Count());
Assert.AreEqual(result.AccessToken, "some-access-token");
}
}
@@ -182,8 +180,8 @@ public void NoCacheLookup()
task.Wait();
AuthenticationResult result = task.Result;
Assert.IsNotNull(result);
- Assert.AreEqual(1, ((ITokenCacheInternal)cache).Accessor.RefreshTokenCount);
- Assert.AreEqual(2, ((ITokenCacheInternal)cache).Accessor.AccessTokenCount);
+ Assert.AreEqual(1, ((ITokenCacheInternal)cache).Accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(2, ((ITokenCacheInternal)cache).Accessor.GetAllAccessTokens().Count());
Assert.AreEqual(result.AccessToken, "some-access-token");
Assert.IsNotNull(
@@ -243,159 +241,6 @@ public void RedirectUriContainsFragmentErrorTest()
}
}
- [TestMethod]
- [TestCategory("InteractiveRequestTests")]
- public void OAuthClient_FailsWithServiceExceptionWhenItCannotParseJsonResponse()
- {
- ValidateOathClient(
- MockHelpers.CreateTooManyRequestsNonJsonResponse(),
- exception =>
- {
- MsalServiceException serverEx = exception.InnerException as MsalServiceException;
- Assert.IsNotNull(serverEx);
- Assert.AreEqual(429, serverEx.StatusCode);
- Assert.AreEqual(MockHelpers.TooManyRequestsContent, serverEx.ResponseBody);
- Assert.AreEqual(MockHelpers.TestRetryAfterDuration, serverEx.Headers.RetryAfter.Delta);
- Assert.AreEqual(MsalError.NonParsableOAuthError, serverEx.ErrorCode);
- });
- }
-
- [TestMethod]
- [TestCategory("InteractiveRequestTests")]
- public void OAuthClient_FailsWithServiceExceptionWhenItCanParseJsonResponse()
- {
- ValidateOathClient(
- MockHelpers.CreateTooManyRequestsJsonResponse(),
- exception =>
- {
- MsalServiceException serverEx = exception.InnerException as MsalServiceException;
- Assert.IsNotNull(serverEx);
- Assert.AreEqual(429, serverEx.StatusCode);
- Assert.AreEqual(MockHelpers.TestRetryAfterDuration, serverEx.Headers.RetryAfter.Delta);
- Assert.AreEqual("Server overload", serverEx.ErrorCode);
- });
- }
-
- [TestMethod]
- [TestCategory("InteractiveRequestTests")]
- public void OAuthClient_FailsWithServiceExceptionWhenEntireResponseIsNull()
- {
- ValidateOathClient(
- null,
- exception =>
- {
- var innerException = exception.InnerException as InvalidOperationException;
- Assert.IsNotNull(innerException);
- });
- }
-
- [TestMethod]
- [TestCategory("InteractiveRequestTests")]
- public void OAuthClient_FailsWithServiceExceptionWhenResponseIsEmpty()
- {
- ValidateOathClient(
- MockHelpers.CreateEmptyResponseMessage(),
- exception =>
- {
- var serverEx = exception.InnerException as MsalServiceException;
- Assert.IsNotNull(serverEx);
- Assert.AreEqual((int)HttpStatusCode.BadRequest, serverEx.StatusCode);
- Assert.IsNotNull(serverEx.ResponseBody);
- Assert.AreEqual(MsalError.HttpStatusCodeNotOk, serverEx.ErrorCode);
- });
- }
-
- [TestMethod]
- [TestCategory("InteractiveRequestTests")]
- public void OAuthClient_FailsWithServiceExceptionWhenResponseIsNull()
- {
- ValidateOathClient(
- MockHelpers.CreateNullResponseMessage(),
- exception =>
- {
- var serverEx = exception.InnerException as MsalServiceException;
- Assert.IsNotNull(serverEx);
- Assert.AreEqual((int)HttpStatusCode.BadRequest, serverEx.StatusCode);
- Assert.IsNull(serverEx.ResponseBody);
- Assert.AreEqual(MsalError.HttpStatusCodeNotOk, serverEx.ErrorCode);
- });
- }
-
- [TestMethod]
- [TestCategory("InteractiveRequestTests")]
- public void OAuthClient_FailsWithServiceExceptionWhenResponseDoesNotContainAnErrorField()
- {
- ValidateOathClient(
- MockHelpers.CreateNoErrorFieldResponseMessage(),
- exception =>
- {
- var serverEx = exception.InnerException as MsalServiceException;
- Assert.IsNotNull(serverEx);
- Assert.AreEqual((int)HttpStatusCode.BadRequest, serverEx.StatusCode);
- Assert.IsNotNull(serverEx.ResponseBody);
- Assert.AreEqual(MsalError.HttpStatusCodeNotOk, serverEx.ErrorCode);
- });
- }
-
- [TestMethod]
- [TestCategory("InteractiveRequestTests")]
- public void OAuthClient_FailsWithServiceExceptionWhenResponseIsHttpNotFound()
- {
- ValidateOathClient(
- MockHelpers.CreateHttpStatusNotFoundResponseMessage(),
- exception =>
- {
- var serverEx = exception.InnerException as MsalServiceException;
- Assert.IsNotNull(serverEx);
- Assert.AreEqual((int)HttpStatusCode.NotFound, serverEx.StatusCode);
- Assert.IsNotNull(serverEx.ResponseBody);
- Assert.AreEqual(MsalError.HttpStatusNotFound, serverEx.ErrorCode);
- });
- }
-
- internal delegate void OAuthClientValidationHandler(Exception ex);
-
- private static void ValidateOathClient(HttpResponseMessage httpResponseMessage, OAuthClientValidationHandler validationHandler)
- {
- using (MockHttpAndServiceBundle harness = new MockHttpAndServiceBundle())
- {
- harness.HttpManager.AddMockHandler(
- new MockHttpMessageHandler
- {
- ExpectedMethod = HttpMethod.Get,
- ResponseMessage = httpResponseMessage
- });
-
- AuthenticationRequestParameters parameters = harness.CreateAuthenticationRequestParameters(
- MsalTestConstants.AuthorityHomeTenant,
- MsalTestConstants.Scope,
- null);
- parameters.RedirectUri = new Uri("some://uri");
- parameters.LoginHint = MsalTestConstants.DisplayableId;
- AcquireTokenInteractiveParameters interactiveParameters = new AcquireTokenInteractiveParameters
- {
- Prompt = Prompt.SelectAccount,
- ExtraScopesToConsent = MsalTestConstants.ScopeForAnotherResource.ToArray(),
- };
-
- InteractiveRequest request = new InteractiveRequest(
- harness.ServiceBundle,
- parameters,
- interactiveParameters,
- new MockWebUI());
-
- try
- {
- request.ExecuteAsync(CancellationToken.None).Wait();
- Assert.Fail("MsalException should have been thrown here");
- }
- catch (Exception exc)
- {
- validationHandler(exc);
- }
- }
- }
-
[TestMethod]
[TestCategory("InteractiveRequestTests")]
public void VerifyAuthorizationResultTest()
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/Resources/ExpectedTokenCache.json b/tests/Microsoft.Identity.Test.Unit.net45/Resources/ExpectedTokenCache.json
new file mode 100644
index 0000000000..f7616bb7c3
--- /dev/null
+++ b/tests/Microsoft.Identity.Test.Unit.net45/Resources/ExpectedTokenCache.json
@@ -0,0 +1,201 @@
+{
+ "AccessToken": {
+ "my-uid.my-utid-env_1-accesstoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3-the_tenant_id-r1/scope1 r1/scope2": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_1",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "AccessToken",
+ "realm": "the_tenant_id",
+ "target": "r1/scope1 r1/scope2",
+ "user_assertion_hash": "assertion hash",
+ "cached_at": "34567",
+ "expires_on": "12345",
+ "extended_expires_on": "23456",
+ "ext_expires_on": "23456"
+ },
+ "my-uid.my-utid-env_2-accesstoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3-the_tenant_id-r1/scope1 r1/scope2": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_2",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "AccessToken",
+ "realm": "the_tenant_id",
+ "target": "r1/scope1 r1/scope2",
+ "user_assertion_hash": "assertion hash",
+ "cached_at": "34567",
+ "expires_on": "12345",
+ "extended_expires_on": "23456",
+ "ext_expires_on": "23456"
+ },
+ "my-uid.my-utid-env_3-accesstoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3-the_tenant_id-r1/scope1 r1/scope2": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_3",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "AccessToken",
+ "realm": "the_tenant_id",
+ "target": "r1/scope1 r1/scope2",
+ "user_assertion_hash": "assertion hash",
+ "cached_at": "34567",
+ "expires_on": "12345",
+ "extended_expires_on": "23456",
+ "ext_expires_on": "23456"
+ },
+ "my-uid.my-utid-env_4-accesstoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3-the_tenant_id-r1/scope1 r1/scope2": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_4",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "AccessToken",
+ "realm": "the_tenant_id",
+ "target": "r1/scope1 r1/scope2",
+ "user_assertion_hash": "assertion hash",
+ "cached_at": "34567",
+ "expires_on": "12345",
+ "extended_expires_on": "23456",
+ "ext_expires_on": "23456"
+ },
+ "my-uid.my-utid-env_5-accesstoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3-the_tenant_id-r1/scope1 r1/scope2": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_5",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "AccessToken",
+ "realm": "the_tenant_id",
+ "target": "r1/scope1 r1/scope2",
+ "user_assertion_hash": "assertion hash",
+ "cached_at": "34567",
+ "expires_on": "12345",
+ "extended_expires_on": "23456",
+ "ext_expires_on": "23456"
+ }
+ },
+ "RefreshToken": {
+ "my-uid.my-utid-env_1-refreshtoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3--": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_1",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "RefreshToken",
+ "family_id": null
+ },
+ "my-uid.my-utid-env_2-refreshtoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3--": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_2",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "RefreshToken",
+ "family_id": null
+ },
+ "my-uid.my-utid-env_3-refreshtoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3--": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_3",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "RefreshToken",
+ "family_id": null
+ },
+ "my-uid.my-utid-env-refreshtoken-1--": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "RefreshToken",
+ "family_id": "1"
+ }
+ },
+ "IdToken": {
+ "my-uid.my-utid-env_1-idtoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3-the_tenant_id-": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_1",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "IdToken",
+ "realm": "the_tenant_id"
+ },
+ "my-uid.my-utid-env_2-idtoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3-the_tenant_id-": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_2",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "IdToken",
+ "realm": "the_tenant_id"
+ },
+ "my-uid.my-utid-env_3-idtoken-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3-the_tenant_id-": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_3",
+ "client_info": null,
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "secret": "access_token_secret",
+ "credential_type": "IdToken",
+ "realm": "the_tenant_id"
+ }
+ },
+ "Account": {
+ "my-uid.my-utid-env_1-the_tenant_id": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_1",
+ "client_info": null,
+ "username": "joe@localhost.com",
+ "name": "First Last",
+ "given_name": "Joe",
+ "family_name": "Doe",
+ "local_account_id": "test_local_account_id",
+ "authority_type": "authority type",
+ "realm": "the_tenant_id"
+ },
+ "my-uid.my-utid-env_2-the_tenant_id": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_2",
+ "client_info": null,
+ "username": "joe@localhost.com",
+ "name": "First Last",
+ "given_name": "Joe",
+ "family_name": "Doe",
+ "local_account_id": "test_local_account_id",
+ "authority_type": "authority type",
+ "realm": "the_tenant_id"
+ },
+ "my-uid.my-utid-env_3-the_tenant_id": {
+ "home_account_id": "my-uid.my-utid",
+ "environment": "env_3",
+ "client_info": null,
+ "username": "joe@localhost.com",
+ "name": "First Last",
+ "given_name": "Joe",
+ "family_name": "Doe",
+ "local_account_id": "test_local_account_id",
+ "authority_type": "authority type",
+ "realm": "the_tenant_id"
+ }
+ },
+ "AppMetadata": {
+ "appmetadata-env_1-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3": {
+ "environment": "env_1",
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "family_id": "1"
+ },
+ "appmetadata-env_2-d3adb33f-c0de-ed0c-c0de-deadb33fc0d3": {
+ "environment": "env_2",
+ "client_id": "d3adb33f-c0de-ed0c-c0de-deadb33fc0d3",
+ "family_id": ""
+ },
+ "appmetadata-env_1-d3adb33f-c1de-ed1c-c1de-deadb33fc1d3": {
+ "environment": "env_1",
+ "client_id": "d3adb33f-c1de-ed1c-c1de-deadb33fc1d3",
+ "family_id": "another_family"
+ }
+ }
+}
diff --git a/tests/Microsoft.Identity.Test.Unit.net45/TestExtensions.cs b/tests/Microsoft.Identity.Test.Unit.net45/TestExtensions.cs
new file mode 100644
index 0000000000..b64625af89
--- /dev/null
+++ b/tests/Microsoft.Identity.Test.Unit.net45/TestExtensions.cs
@@ -0,0 +1,52 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using System.Linq;
+using Microsoft.Identity.Client.Cache;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.Identity.Test.Unit
+{
+ public static class TestExtensions
+ {
+ internal static void AssertItemCount(
+ this ITokenCacheAccessor accessor,
+ int expectedAtCount,
+ int expectedRtCount,
+ int expectedIdtCount,
+ int expectedAccountCount,
+ int expectedAppMetadataCount = 0)
+ {
+ Assert.AreEqual(expectedAtCount, accessor.GetAllAccessTokens().Count());
+ Assert.AreEqual(expectedRtCount, accessor.GetAllRefreshTokens().Count());
+ Assert.AreEqual(expectedIdtCount, accessor.GetAllIdTokens().Count());
+ Assert.AreEqual(expectedAccountCount, accessor.GetAllAccounts().Count());
+ Assert.AreEqual(expectedAppMetadataCount, accessor.GetAllAppMetadata().Count());
+ }
+
+ }
+}
diff --git a/tests/devapps/NetFxConsoleTestApp/App.config b/tests/devapps/NetFxConsoleTestApp/App.config
new file mode 100644
index 0000000000..56efbc7b5f
--- /dev/null
+++ b/tests/devapps/NetFxConsoleTestApp/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/desktop/SampleApp/SampleApp.csproj b/tests/devapps/NetFxConsoleTestApp/NetFxConsoleTestApp.csproj
similarity index 53%
rename from samples/desktop/SampleApp/SampleApp.csproj
rename to tests/devapps/NetFxConsoleTestApp/NetFxConsoleTestApp.csproj
index cb3f6df322..06151a9807 100644
--- a/samples/desktop/SampleApp/SampleApp.csproj
+++ b/tests/devapps/NetFxConsoleTestApp/NetFxConsoleTestApp.csproj
@@ -4,13 +4,14 @@
Debug
AnyCPU
- {85397CDA-120A-4626-9865-AD79EBFAC794}
- WinExe
- SampleApp
- SampleApp
- v4.6.1
+ {24D2AF71-A01E-4450-8C3F-B9923A81134B}
+ Exe
+ NetFxConsoleTestApp
+ NetFxConsoleTestApp
+ v4.7.2
512
-
+ true
+ true
AnyCPU
@@ -31,9 +32,6 @@
prompt
4
-
- true
-
@@ -41,56 +39,19 @@
-
-
-
-
-
- Form
-
-
- MainForm.cs
-
-
-
- MainForm.cs
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
- Designer
-
-
- True
- Resources.resx
-
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
-
- True
- Settings.settings
- True
-
-
-
- All
-
-
- {3433eb33-114a-4db7-bc57-14f17f55da3c}
+ {60117a9b-4bb8-472e-bfff-52cbf67ca95a}
Microsoft.Identity.Client
diff --git a/tests/devapps/NetFxConsoleTestApp/Program.cs b/tests/devapps/NetFxConsoleTestApp/Program.cs
new file mode 100644
index 0000000000..f1b36e670d
--- /dev/null
+++ b/tests/devapps/NetFxConsoleTestApp/Program.cs
@@ -0,0 +1,277 @@
+//----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Security;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
+using Microsoft.Identity.Client;
+using Microsoft.Identity.Client.AppConfig;
+
+namespace NetCoreTestApp
+{
+ public class Program
+ {
+ // TODO: replace with FOCI family members IDs
+ // DO NOT CHECK THESE IN
+ private const string FAMILY_MEMBER_1 = ""; // Office
+ private const string FAMILY_MEMBER_2 = ""; // Teams
+ private const string NON_FAMILY_MEMBER = "0615b6ca-88d4-4884-8729-b178178f7c27";
+
+
+ private static readonly string[] s_scopes = new[] { "https://graph.microsoft.com/.default" };
+
+ private static readonly string s_cacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + ".msalcache.json";
+
+ // These 2 apps share the cache
+ private static IPublicClientApplication s_pcaFam1;
+ private static IPublicClientApplication s_pcaFam2;
+ private static IPublicClientApplication s_pcaNonFam;
+
+ public static void Main(string[] args)
+ {
+ s_pcaFam1 = PublicClientApplicationBuilder
+ .Create(FAMILY_MEMBER_1)
+ .WithLogging(Log, LogLevel.Verbose, true)
+ .Build();
+
+ s_pcaFam2 = PublicClientApplicationBuilder
+ .Create(FAMILY_MEMBER_2)
+ .WithLogging(Log, LogLevel.Verbose, true)
+ .Build();
+
+ s_pcaNonFam = PublicClientApplicationBuilder
+ .Create(NON_FAMILY_MEMBER)
+ .WithLogging(Log, LogLevel.Verbose, true)
+ .Build();
+
+
+ SetCacheSerializationToFile(s_pcaFam1);
+ SetCacheSerializationToFile(s_pcaFam2);
+ SetCacheSerializationToFile(s_pcaNonFam);
+
+ RunConsoleAppLogicAsync().Wait();
+ }
+
+ private static void SetCacheSerializationToFile(IPublicClientApplication pca1)
+ {
+ pca1.UserTokenCache.SetBeforeAccess(notificationArgs =>
+ {
+ notificationArgs.TokenCache.DeserializeMsalV3(File.Exists(s_cacheFilePath)
+ ? File.ReadAllBytes(s_cacheFilePath)
+ : null);
+ });
+ pca1.UserTokenCache.SetAfterAccess(notificationArgs =>
+ {
+ // if the access operation resulted in a cache update
+ if (notificationArgs.HasStateChanged)
+ {
+ // reflect changes in the persistent store
+ File.WriteAllBytes(s_cacheFilePath, notificationArgs.TokenCache.SerializeMsalV3());
+ }
+ });
+ }
+
+ private static async Task RunConsoleAppLogicAsync()
+ {
+ while (true)
+ {
+ Console.Clear();
+
+ await DisplayAccountsAsync(s_pcaFam1).ConfigureAwait(false);
+
+ // display menu
+ Console.WriteLine(@"
+ 1. Acquire Token App1 (family member)
+ 2. Acquire Token App2 (family member)
+ 3. Acquire Token App3 (non-family member)
+ 4. Acquire Token Silent App1 (family member)
+ 5. Acquire Token Silent App2 (family member)
+ 6. Acquire Token Silent App3 (non-family member)
+
+
+ 7. Clear cache
+ 0. Exit App
+ Enter your Selection: ");
+ int.TryParse(Console.ReadLine(), out var selection);
+
+ Task authTask = null;
+
+ try
+ {
+ switch (selection)
+ {
+ case 1:
+ authTask = GetInteractiveAuthTaskAsync(s_pcaFam1);
+ FetchTokenAsync(s_pcaNonFam, authTask).GetAwaiter().GetResult();
+ break;
+ case 2:
+ authTask = GetInteractiveAuthTaskAsync(s_pcaFam2);
+ FetchTokenAsync(s_pcaNonFam, authTask).GetAwaiter().GetResult();
+ break;
+ case 3:
+ authTask = GetInteractiveAuthTaskAsync(s_pcaNonFam);
+ FetchTokenAsync(s_pcaNonFam, authTask).GetAwaiter().GetResult();
+ break;
+ case 4:
+ authTask = GetSilentAuthTaskAsync(s_pcaFam1);
+ FetchTokenAsync(s_pcaFam1, authTask).GetAwaiter().GetResult();
+ break;
+ case 5:
+ authTask = GetSilentAuthTaskAsync(s_pcaFam2);
+ FetchTokenAsync(s_pcaNonFam, authTask).GetAwaiter().GetResult();
+ break;
+ case 6:
+ authTask = GetSilentAuthTaskAsync(s_pcaNonFam);
+ FetchTokenAsync(s_pcaNonFam, authTask).GetAwaiter().GetResult();
+ break;
+
+ case 7:
+ var accounts1 = await s_pcaFam1.GetAccountsAsync().ConfigureAwait(false);
+ var accounts2 = await s_pcaFam1.GetAccountsAsync().ConfigureAwait(false);
+ var accounts3 = await s_pcaFam1.GetAccountsAsync().ConfigureAwait(false);
+
+
+ foreach (var acc in accounts1)
+ {
+ await s_pcaFam1.RemoveAsync(acc).ConfigureAwait(false);
+ }
+
+ break;
+ case 0:
+ return;
+ default:
+ break;
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Log(LogLevel.Error, ex.Message, false);
+ Log(LogLevel.Error, ex.StackTrace, false);
+ }
+
+ Console.WriteLine("\n\nHit 'ENTER' to continue...");
+ Console.ReadLine();
+ }
+ }
+
+ private static Task GetInteractiveAuthTaskAsync(IPublicClientApplication pca)
+ {
+ return pca.AcquireTokenInteractive(s_scopes, null).ExecuteAsync();
+ }
+
+ private static Task GetSilentAuthTaskAsync(IPublicClientApplication pca)
+ {
+ // get all serialized accounts
+ // get all RTs WHERE rt.client == app.client OR app is part of family or unkown
+ // JOIN acounts and RTs ON homeAccountID
+
+ // A -> interactive auth -> account, RT1
+ // B -> GetAccounts -> NULL
+
+ var accounts = pca.GetAccountsAsync().GetAwaiter().GetResult();
+ if (accounts.Count() > 1)
+ {
+ Log(LogLevel.Error, "Not expecting to handle multiple accounts", false);
+ return null;
+ }
+
+ //return pca.AcquireTokenSilent(s_scopes, accounts.FirstOrDefault()).ExecuteAsync();
+ return pca.AcquireTokenSilent(s_scopes, "bogavril@microsoft.com").ExecuteAsync();
+
+ }
+
+ private static async Task FetchTokenAsync(IPublicClientApplication pca, Task authTask)
+ {
+ if (authTask == null)
+ {
+ return;
+ }
+
+ await authTask.ConfigureAwait(false);
+
+ Console.BackgroundColor = ConsoleColor.DarkGreen;
+ Console.WriteLine("Token is {0}", authTask.Result.AccessToken);
+ Console.ResetColor();
+
+
+ Console.BackgroundColor = ConsoleColor.DarkMagenta;
+ await DisplayAccountsAsync(pca).ConfigureAwait(false);
+
+ Console.ResetColor();
+ }
+
+
+
+ private static async Task DisplayAccountsAsync(IPublicClientApplication pca)
+ {
+ IEnumerable accounts = await pca.GetAccountsAsync().ConfigureAwait(false);
+
+ Console.WriteLine(string.Format(CultureInfo.CurrentCulture, "For the public client, the tokenCache contains {0} token(s)", accounts.Count()));
+
+ foreach (var account in accounts)
+ {
+ Console.WriteLine("PCA account for: " + account.Username + "\n");
+ }
+ }
+
+ private static void Log(LogLevel level, string message, bool containsPii)
+ {
+ if (!containsPii)
+ {
+ Console.BackgroundColor = ConsoleColor.DarkBlue;
+ }
+
+ switch (level)
+ {
+ case LogLevel.Error:
+ Console.ForegroundColor = ConsoleColor.Red;
+ break;
+ case LogLevel.Warning:
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ break;
+ case LogLevel.Verbose:
+ Console.ForegroundColor = ConsoleColor.Gray;
+ break;
+ default:
+ break;
+ }
+
+ Console.WriteLine($"{level} {message}");
+ Console.ResetColor();
+ }
+
+
+
+
+ }
+}
diff --git a/tests/devapps/NetFxConsoleTestApp/Properties/AssemblyInfo.cs b/tests/devapps/NetFxConsoleTestApp/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..351596c94f
--- /dev/null
+++ b/tests/devapps/NetFxConsoleTestApp/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NetFxConsoleTestApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("NetFxConsoleTestApp")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("24d2af71-a01e-4450-8c3f-b9923a81134b")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/devapps/XForms/XForms/SettingsPage.xaml.cs b/tests/devapps/XForms/XForms/SettingsPage.xaml.cs
index 0ec0a0f02d..f47a780dda 100644
--- a/tests/devapps/XForms/XForms/SettingsPage.xaml.cs
+++ b/tests/devapps/XForms/XForms/SettingsPage.xaml.cs
@@ -27,6 +27,7 @@
using System;
using System.Globalization;
+using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@@ -50,10 +51,10 @@ private void RefreshView()
authority.Text = App.Authority;
clientIdEntry.Text = App.ClientId;
- numOfAtItems.Text = App.MsalPublicClient.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count.ToString(CultureInfo.InvariantCulture);
- numOfRtItems.Text = App.MsalPublicClient.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count.ToString(CultureInfo.InvariantCulture);
- numOfIdItems.Text = App.MsalPublicClient.UserTokenCacheInternal.Accessor.GetAllIdTokens().Count.ToString(CultureInfo.InvariantCulture);
- numOfAccountItems.Text = App.MsalPublicClient.UserTokenCacheInternal.Accessor.GetAllAccounts().Count.ToString(CultureInfo.InvariantCulture);
+ numOfAtItems.Text = App.MsalPublicClient.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count().ToString(CultureInfo.InvariantCulture);
+ numOfRtItems.Text = App.MsalPublicClient.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count().ToString(CultureInfo.InvariantCulture);
+ numOfIdItems.Text = App.MsalPublicClient.UserTokenCacheInternal.Accessor.GetAllIdTokens().Count().ToString(CultureInfo.InvariantCulture);
+ numOfAccountItems.Text = App.MsalPublicClient.UserTokenCacheInternal.Accessor.GetAllAccounts().Count().ToString(CultureInfo.InvariantCulture);
validateAuthoritySwitch.IsToggled = App.ValidateAuthority;
RedirectUriLabel.Text = App.MsalPublicClient.AppConfig.RedirectUri;