Initial version of U/P as secondary provider (#1)
* Initial version of U/P as secondary provider * Add documentation * Update documentation * "Updates per CR"
This commit is contained in:
Родитель
c7348fc75f
Коммит
bf3367a17f
15
README.md
15
README.md
|
@ -1,7 +1,18 @@
|
|||
# ADFS Authentication Adapters
|
||||
|
||||
# Contributing
|
||||
## Overview
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
This repository contains custom authentication adapters that you can use with ADFS. The following adapters are currently included:
|
||||
|
||||
1. __[UsernamePasswordSecondFactor](UsernamePasswordSecondFactor)__ - External authentication adapter for performing Username + Password
|
||||
authentication for MFA.
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. We encourage you to fork this project, include any authentication providers you find
|
||||
useful, and then do a pull request to master. If your providers work, we'll include them so everyone can benefit.
|
||||
|
||||
Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Username/Password MFA Authentication Adapters
|
||||
|
||||
## Overview
|
||||
|
||||
This project
|
||||
|
||||
## Requirements
|
||||
|
||||
<What do they need to do to get started>
|
||||
<Link to any documentation on adding external auth providers to ADFS>
|
||||
|
||||
## Getting Started
|
||||
|
||||
<Steps to download, build, test, and deploy the project>
|
||||
|
||||
## Code Overview
|
||||
|
||||
<Optional, but good to have for more complicated projects>
|
||||
<Note, you can include images, UML diagrams, whatever in GitHub .md files>
|
||||
|
||||
## Codesigning the Binary
|
||||
|
||||
<Details>
|
||||
|
||||
|
||||
## Contributing (Special Note)
|
||||
|
||||
If you are contributing code, please be sure that you __remove any signing key__ from any code you
|
||||
put in a pull request. This project is public, and anyone on the Internet can see it.
|
||||
|
||||
For the full Contributing details, please see __[the root README](../README.md)__.
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26430.15
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsernamePasswordSecondFactor", "UsernamePasswordSecondFactor\UsernamePasswordSecondFactor.csproj", "{CFB51980-393E-4212-BC77-9C266978BCF8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CFB51980-393E-4212-BC77-9C266978BCF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CFB51980-393E-4212-BC77-9C266978BCF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CFB51980-393E-4212-BC77-9C266978BCF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CFB51980-393E-4212-BC77-9C266978BCF8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace UsernamePasswordSecondFactor
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public const string UsernamePasswordMfa = "http://schemas.microsoft.com/ws/2012/12/authmethod/usernamepasswordMFA";
|
||||
|
||||
public const string AuthenticationMethodClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod";
|
||||
public const string WindowsAccountNameClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname";
|
||||
public const string UpnClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn";
|
||||
|
||||
public static class AuthContextKeys
|
||||
{
|
||||
public const string SessionId = "sessionid";
|
||||
}
|
||||
|
||||
public static class DynamicContentLabels
|
||||
{
|
||||
public const string markerUserName = "%LoginPageUserName%";
|
||||
public const string markerOverallError = "%PageErrorOverall%";
|
||||
public const string markerActionUrl = "%PageActionUrl%";
|
||||
public const string markerPageIntroductionTitle = "%PageIntroductionTitle%";
|
||||
public const string markerPageIntroductionText = "%PageIntroductionText%";
|
||||
public const string markerPageTitle = "%PageTitle%";
|
||||
public const string markerSubmitButton = "%PageSubmitButtonLabel%";
|
||||
public const string markerChoiceSuccess = "%ChoiceSuccess%";
|
||||
public const string markerChoiceFail = "%ChoiceFail%";
|
||||
public const string markerUserChoice = "%UserChoice%";
|
||||
public const string markerLoginPageUserNameExample = "%LoginPageUserNameExample%";
|
||||
public const string markerLoginPagePasswordLabel = "%LoginPagePasswordLabel%";
|
||||
|
||||
}
|
||||
|
||||
public static class ResourceNames
|
||||
{
|
||||
public const string AdminFriendlyName = "AdminFriendlyName";
|
||||
public const string Description = "Description";
|
||||
public const string FriendlyName = "FriendlyName";
|
||||
public const string PageIntroductionTitle = "PageIntroductionTitle";
|
||||
public const string PageIntroductionText = "PageIntroductionText";
|
||||
public const string AuthPageTemplate = "AuthPage";
|
||||
public const string PageTitle = "PageTitle";
|
||||
public const string SubmitButtonLabel = "SubmitButtonLabel";
|
||||
public const string AuthenticationFailed = "AuthenticationFailed";
|
||||
public const string ErrorInvalidSessionId = "ErrorInvalidSessionId";
|
||||
public const string ErrorInvalidContext = "ErrorInvalidContext";
|
||||
public const string ErrorNoUserIdentity = "ErrorNoUserIdentity";
|
||||
public const string ErrorNoAnswerProvided = "ErrorNoAnswerProvided";
|
||||
public const string ErrorFailSelected = "ErrorFailSelected";
|
||||
public const string ChoiceSuccess = "ChoiceSuccess";
|
||||
public const string ChoiceFail = "ChoiceFail";
|
||||
public const string UserChoice = "UserChoice";
|
||||
public const string FailedLogin = "FailedLogin";
|
||||
}
|
||||
|
||||
public static class PropertyNames
|
||||
{
|
||||
public const string UserSelection = "UserSelection";
|
||||
public const string AuthenticationMethod = "AuthMethod";
|
||||
public const string Password = "PasswordInput";
|
||||
public const string Username = "Username";
|
||||
}
|
||||
|
||||
public static class Lcid
|
||||
{
|
||||
public const int Enus = 0x409; // for test only, proper localization should input parent locale, e.g. "en" in this case.
|
||||
public const int Fr = 0xC; // for test only, no FR resources are embedded.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.DirectoryServices.AccountManagement;
|
||||
|
||||
namespace UsernamePasswordSecondFactor
|
||||
{
|
||||
public class PasswordValidator
|
||||
{
|
||||
static readonly PrincipalContext _ctx = new PrincipalContext(ContextType.Domain);
|
||||
|
||||
public static bool Validate(string username, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _ctx.ValidateCredentials(username, password);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new UsernamePasswordValidationException("failed to validate password");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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("UsernamePasswordSecondFactor")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("UsernamePasswordSecondFactor")]
|
||||
[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("cfb51980-393e-4212-bc77-9c266978bcf8")]
|
||||
|
||||
// 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")]
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using UsernamePasswordSecondFactor.Resources;
|
||||
|
||||
namespace UsernamePasswordSecondFactor
|
||||
{
|
||||
internal static class ResourceHandler
|
||||
{
|
||||
public static string GetResource(string resourceName, int lcid)
|
||||
{
|
||||
if (String.IsNullOrEmpty(resourceName))
|
||||
{
|
||||
throw new ArgumentNullException("resourceName");
|
||||
}
|
||||
|
||||
return StringResources.ResourceManager.GetString(resourceName, new CultureInfo(lcid));
|
||||
}
|
||||
}
|
||||
}
|
236
UsernamePasswordSecondFactor/UsernamePasswordSecondFactor/Resources/StringResources.Designer.cs
сгенерированный
Normal file
236
UsernamePasswordSecondFactor/UsernamePasswordSecondFactor/Resources/StringResources.Designer.cs
сгенерированный
Normal file
|
@ -0,0 +1,236 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace UsernamePasswordSecondFactor.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class StringResources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal StringResources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[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("UsernamePasswordSecondFactor.Resources.StringResources", typeof(StringResources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Forms Authentication as Secondary Authentication.
|
||||
/// </summary>
|
||||
internal static string AdminFriendlyName {
|
||||
get {
|
||||
return ResourceManager.GetString("AdminFriendlyName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to AuthenticationFailed.
|
||||
/// </summary>
|
||||
internal static string AuthenticationFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("AuthenticationFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to <div id="loginArea">
|
||||
///<div id="loginMessage" class="groupMargin">%PageIntroductionTitle%</div>
|
||||
///
|
||||
///<script type="text/javascript" language="JavaScript">
|
||||
/////<![CDATA[
|
||||
///
|
||||
/// function Login() {
|
||||
/// }
|
||||
///
|
||||
/// Login.userNameInput = 'userNameInput';
|
||||
/// Login.passwordInput = 'passwordInput';
|
||||
///
|
||||
/// Login.initialize = function () {
|
||||
///
|
||||
/// }();
|
||||
///
|
||||
/// Login.submitLoginRequest = function () {
|
||||
///
|
||||
/// console.log("Start");
|
||||
/// var userName = document.getElementById(Login.userNameInput);
|
||||
/// var password = document.getElementById(Log [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string AuthPage {
|
||||
get {
|
||||
return ResourceManager.GetString("AuthPage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ChoiceFail.
|
||||
/// </summary>
|
||||
internal static string ChoiceFail {
|
||||
get {
|
||||
return ResourceManager.GetString("ChoiceFail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ChoiceSuccess.
|
||||
/// </summary>
|
||||
internal static string ChoiceSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("ChoiceSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Forms Authentication as Secondary Authentication.
|
||||
/// </summary>
|
||||
internal static string Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ErrorFailSelected.
|
||||
/// </summary>
|
||||
internal static string ErrorFailSelected {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorFailSelected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ErrorInvalidContext.
|
||||
/// </summary>
|
||||
internal static string ErrorInvalidContext {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorInvalidContext", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ErrorInvalidSessionId.
|
||||
/// </summary>
|
||||
internal static string ErrorInvalidSessionId {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorInvalidSessionId", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ErrorNoAnswerProvided.
|
||||
/// </summary>
|
||||
internal static string ErrorNoAnswerProvided {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNoAnswerProvided", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ErrorNoUserIdentity.
|
||||
/// </summary>
|
||||
internal static string ErrorNoUserIdentity {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNoUserIdentity", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your password is invalid, try again..
|
||||
/// </summary>
|
||||
internal static string FailedLogin {
|
||||
get {
|
||||
return ResourceManager.GetString("FailedLogin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to .
|
||||
/// </summary>
|
||||
internal static string PageIntroductionText {
|
||||
get {
|
||||
return ResourceManager.GetString("PageIntroductionText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enter your password.
|
||||
/// </summary>
|
||||
internal static string PageIntroductionTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("PageIntroductionTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to PageTitle.
|
||||
/// </summary>
|
||||
internal static string PageTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("PageTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sign in.
|
||||
/// </summary>
|
||||
internal static string SubmitButtonLabel {
|
||||
get {
|
||||
return ResourceManager.GetString("SubmitButtonLabel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to UserChoice.
|
||||
/// </summary>
|
||||
internal static string UserChoice {
|
||||
get {
|
||||
return ResourceManager.GetString("UserChoice", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AdminFriendlyName" xml:space="preserve">
|
||||
<value>Forms Authentication as Secondary Authentication</value>
|
||||
</data>
|
||||
<data name="AuthenticationFailed" xml:space="preserve">
|
||||
<value>AuthenticationFailed</value>
|
||||
</data>
|
||||
<data name="AuthPage" xml:space="preserve">
|
||||
<value><div id="loginArea">
|
||||
<div id="loginMessage" class="groupMargin">%PageIntroductionTitle%</div>
|
||||
|
||||
<script type="text/javascript" language="JavaScript">
|
||||
//<![CDATA[
|
||||
|
||||
function Login() {
|
||||
}
|
||||
|
||||
Login.userNameInput = 'userNameInput';
|
||||
Login.passwordInput = 'passwordInput';
|
||||
|
||||
Login.initialize = function () {
|
||||
|
||||
}();
|
||||
|
||||
Login.submitLoginRequest = function () {
|
||||
|
||||
console.log("Start");
|
||||
var userName = document.getElementById(Login.userNameInput);
|
||||
var password = document.getElementById(Login.passwordInput);
|
||||
|
||||
if (!userName.value || !userName.value.match('[@\\\\]')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!password.value) {
|
||||
return false;
|
||||
}
|
||||
console.log("Pre-Submit");
|
||||
document.forms['loginForm'].submit();
|
||||
console.log("End");
|
||||
return false;
|
||||
};
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
|
||||
<form method="post" id="loginForm" autocomplete="off" novalidate="novalidate" onKeyPress="if (event && event.keyCode == 13) Login.submitLoginRequest();" action="%PageActionUrl%" >
|
||||
<div id="error" class="fieldMargin error smallText">
|
||||
<label id="errorText" for="%LoginPageErrorCause%">%PageIntroductionText%</label>
|
||||
</div>
|
||||
|
||||
<!-- These inputs are required by the presentation framework. Do not modify or remove -->
|
||||
<input id="authMethod" type="hidden" name="AuthMethod" value="%AuthMethod%"/>
|
||||
<input id="context" type="hidden" name="Context" value="%Context%"/>
|
||||
<!-- End inputs are required by the presentation framework. -->
|
||||
|
||||
<div id="formsAuthenticationArea">
|
||||
<div id="userNameArea">
|
||||
<input id="userNameInput" name="UserName" type="hidden" value="%LoginPageUserNameExample%" tabindex="1" class="text fullWidth"
|
||||
spellcheck="false" autocomplete="off" readonly/>
|
||||
</div>
|
||||
|
||||
<div id="passwordArea">
|
||||
<input id="passwordInput" name="PasswordInput" type="password" tabindex="2" class="text fullWidth"
|
||||
placeholder="%LoginPagePasswordLabel%" autocomplete="off"/>
|
||||
</div>
|
||||
<div id="submissionArea" class="submitMargin">
|
||||
<span id="submitButton" class="submit" tabindex="4"
|
||||
onKeyPress="if (event && event.keyCode == 32) Login.submitLoginRequest();"
|
||||
onclick="Login.submitLoginRequest();">%PageSubmitButtonLabel%</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div></value>
|
||||
</data>
|
||||
<data name="ChoiceFail" xml:space="preserve">
|
||||
<value>ChoiceFail</value>
|
||||
</data>
|
||||
<data name="ChoiceSuccess" xml:space="preserve">
|
||||
<value>ChoiceSuccess</value>
|
||||
</data>
|
||||
<data name="Description" xml:space="preserve">
|
||||
<value>Forms Authentication as Secondary Authentication</value>
|
||||
</data>
|
||||
<data name="ErrorFailSelected" xml:space="preserve">
|
||||
<value>ErrorFailSelected</value>
|
||||
</data>
|
||||
<data name="ErrorInvalidContext" xml:space="preserve">
|
||||
<value>ErrorInvalidContext</value>
|
||||
</data>
|
||||
<data name="ErrorInvalidSessionId" xml:space="preserve">
|
||||
<value>ErrorInvalidSessionId</value>
|
||||
</data>
|
||||
<data name="ErrorNoAnswerProvided" xml:space="preserve">
|
||||
<value>ErrorNoAnswerProvided</value>
|
||||
</data>
|
||||
<data name="ErrorNoUserIdentity" xml:space="preserve">
|
||||
<value>ErrorNoUserIdentity</value>
|
||||
</data>
|
||||
<data name="FailedLogin" xml:space="preserve">
|
||||
<value>Your password is invalid, try again.</value>
|
||||
</data>
|
||||
<data name="PageIntroductionText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PageIntroductionTitle" xml:space="preserve">
|
||||
<value>Enter your password</value>
|
||||
</data>
|
||||
<data name="PageTitle" xml:space="preserve">
|
||||
<value>PageTitle</value>
|
||||
</data>
|
||||
<data name="SubmitButtonLabel" xml:space="preserve">
|
||||
<value>Sign in</value>
|
||||
</data>
|
||||
<data name="UserChoice" xml:space="preserve">
|
||||
<value>UserChoice</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using Microsoft.IdentityServer.Web.Authentication.External;
|
||||
using Claim = System.Security.Claims.Claim;
|
||||
|
||||
namespace UsernamePasswordSecondFactor
|
||||
{
|
||||
public class UsernamePasswordAdapter : IAuthenticationAdapter
|
||||
{
|
||||
protected IAdapterPresentationForm CreateAdapterPresentation(string username)
|
||||
{
|
||||
return new UsernamePasswordPresentation(username);
|
||||
}
|
||||
|
||||
protected IAdapterPresentationForm CreateAdapterPresentationOnError(string username, ExternalAuthenticationException ex)
|
||||
{
|
||||
return new UsernamePasswordPresentation(username, ex);
|
||||
}
|
||||
|
||||
#region IAuthenticationAdapter Members
|
||||
|
||||
public IAuthenticationAdapterMetadata Metadata => new UsernamePasswordMetadata();
|
||||
|
||||
public IAdapterPresentation BeginAuthentication(Claim identityClaim, HttpListenerRequest request, IAuthenticationContext authContext)
|
||||
{
|
||||
if (null == identityClaim) throw new ArgumentNullException(nameof(identityClaim));
|
||||
|
||||
if (null == authContext) throw new ArgumentNullException(nameof(authContext));
|
||||
|
||||
if (String.IsNullOrEmpty(identityClaim.Value))
|
||||
{
|
||||
throw new InvalidDataException(ResourceHandler.GetResource(Constants.ResourceNames.ErrorNoUserIdentity, authContext.Lcid));
|
||||
}
|
||||
|
||||
return CreateAdapterPresentation(identityClaim.Value);
|
||||
}
|
||||
|
||||
public bool IsAvailableForUser(Claim identityClaim, IAuthenticationContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public IAdapterPresentation OnError(HttpListenerRequest request, ExternalAuthenticationException ex)
|
||||
{
|
||||
if (ex == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ex));
|
||||
}
|
||||
|
||||
return CreateAdapterPresentationOnError(String.Empty,ex);
|
||||
}
|
||||
|
||||
public void OnAuthenticationPipelineLoad(IAuthenticationMethodConfigData configData)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnAuthenticationPipelineUnload()
|
||||
{
|
||||
}
|
||||
|
||||
public IAdapterPresentation TryEndAuthentication(IAuthenticationContext authContext, IProofData proofData, HttpListenerRequest request, out Claim[] outgoingClaims)
|
||||
{
|
||||
if (null == authContext)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(authContext));
|
||||
}
|
||||
|
||||
outgoingClaims = new Claim[0];
|
||||
|
||||
if (proofData?.Properties == null || !proofData.Properties.ContainsKey(Constants.PropertyNames.Password))
|
||||
{
|
||||
throw new ExternalAuthenticationException(ResourceHandler.GetResource(Constants.ResourceNames.ErrorNoAnswerProvided, authContext.Lcid), authContext);
|
||||
}
|
||||
|
||||
string username = (string)proofData.Properties[Constants.PropertyNames.Username];
|
||||
string password = (string)proofData.Properties[Constants.PropertyNames.Password];
|
||||
|
||||
if (PasswordValidator.Validate(username, password))
|
||||
{
|
||||
outgoingClaims = new[]
|
||||
{
|
||||
new Claim(Constants.AuthenticationMethodClaimType, Constants.UsernamePasswordMfa)
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateAdapterPresentationOnError(username, new UsernamePasswordValidationException());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Microsoft.IdentityServer.Web.Authentication.External;
|
||||
|
||||
namespace UsernamePasswordSecondFactor
|
||||
{
|
||||
class UsernamePasswordException : ExternalAuthenticationException
|
||||
{
|
||||
public UsernamePasswordException() : base()
|
||||
{ }
|
||||
|
||||
public UsernamePasswordException(string message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class UsernamePasswordValidationException : UsernamePasswordException
|
||||
{
|
||||
public UsernamePasswordValidationException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public UsernamePasswordValidationException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Microsoft.IdentityServer.Web.Authentication.External;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace UsernamePasswordSecondFactor
|
||||
{
|
||||
public class UsernamePasswordMetadata : IAuthenticationAdapterMetadata
|
||||
{
|
||||
protected string GetMetadataResource(string resourceName, int lcid)
|
||||
{
|
||||
return ResourceHandler.GetResource(resourceName, lcid);
|
||||
}
|
||||
|
||||
private readonly Dictionary<int, string> _descriptions = new Dictionary<int, string>();
|
||||
private readonly Dictionary<int, string> _friendlyNames = new Dictionary<int, string>();
|
||||
|
||||
private readonly int[] _supportedLcids = new[] { Constants.Lcid.Enus, Constants.Lcid.Fr };
|
||||
|
||||
public UsernamePasswordMetadata()
|
||||
{
|
||||
for (int index = 0; index < _supportedLcids.Length; index++)
|
||||
{
|
||||
int lcid = _supportedLcids[index];
|
||||
_descriptions.Add(lcid, GetMetadataResource(Constants.ResourceNames.Description, lcid));
|
||||
_friendlyNames.Add(lcid, GetMetadataResource(Constants.ResourceNames.FriendlyName, lcid));
|
||||
}
|
||||
}
|
||||
|
||||
#region IAuthenticationHandlerMetadata Members
|
||||
|
||||
public string AdminName
|
||||
{
|
||||
get { return GetMetadataResource(Constants.ResourceNames.AdminFriendlyName, CultureInfo.CurrentUICulture.LCID); }
|
||||
}
|
||||
|
||||
public virtual string[] AuthenticationMethods
|
||||
{
|
||||
get { return new[] { Constants.UsernamePasswordMfa }; }
|
||||
}
|
||||
|
||||
public Dictionary<int, string> Descriptions
|
||||
{
|
||||
get { return _descriptions; }
|
||||
}
|
||||
|
||||
public Dictionary<int, string> FriendlyNames
|
||||
{
|
||||
get { return _friendlyNames; }
|
||||
}
|
||||
|
||||
public string[] IdentityClaims
|
||||
{
|
||||
get { return new[] { Constants.UpnClaimType }; }
|
||||
}
|
||||
|
||||
public bool RequiresIdentity
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public int[] AvailableLcids
|
||||
{
|
||||
get { return _supportedLcids; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using UsernamePasswordSecondFactor.Resources;
|
||||
using Microsoft.IdentityServer.Web.Authentication.External;
|
||||
|
||||
|
||||
namespace UsernamePasswordSecondFactor
|
||||
{
|
||||
internal class UsernamePasswordPresentation : IAdapterPresentationForm
|
||||
{
|
||||
private readonly ExternalAuthenticationException _ex = null;
|
||||
|
||||
private readonly string _username = string.Empty;
|
||||
|
||||
private readonly Dictionary<string, string> _dynamicContents = new Dictionary<string, string>()
|
||||
{
|
||||
{Constants.DynamicContentLabels.markerUserName, String.Empty},
|
||||
{Constants.DynamicContentLabels.markerOverallError, String.Empty},
|
||||
{Constants.DynamicContentLabels.markerActionUrl, String.Empty},
|
||||
{Constants.DynamicContentLabels.markerPageIntroductionTitle, String.Empty},
|
||||
{Constants.DynamicContentLabels.markerPageIntroductionText, String.Empty},
|
||||
{Constants.DynamicContentLabels.markerPageTitle, String.Empty},
|
||||
};
|
||||
|
||||
public UsernamePasswordPresentation(string username)
|
||||
{
|
||||
_username = username;
|
||||
}
|
||||
|
||||
public UsernamePasswordPresentation(string username, ExternalAuthenticationException ex) : this(username)
|
||||
{
|
||||
_ex = ex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace template markers with explicitly given replacements.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="replacements"></param>
|
||||
/// <returns></returns>
|
||||
private static string Replace(string input, Dictionary<string, string> replacements)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input) || null == replacements)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
// Use StringBuiler and allocate buffer 3 times larger
|
||||
StringBuilder sb = new StringBuilder(input, input.Length * 3);
|
||||
foreach (string key in replacements.Keys)
|
||||
{
|
||||
sb.Replace(key, replacements[key]);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#region IAdapterPresentationForm Members
|
||||
|
||||
public string GetFormHtml(int lcid)
|
||||
{
|
||||
var dynamicContents = new Dictionary<string, string>(_dynamicContents)
|
||||
{
|
||||
[Constants.DynamicContentLabels.markerPageIntroductionTitle] =
|
||||
GetPresentationResource(Constants.ResourceNames.PageIntroductionTitle, lcid),
|
||||
[Constants.DynamicContentLabels.markerPageIntroductionText] =
|
||||
GetPresentationResource(Constants.ResourceNames.PageIntroductionText, lcid),
|
||||
[Constants.DynamicContentLabels.markerPageTitle] = GetPageTitle(lcid),
|
||||
[Constants.DynamicContentLabels.markerSubmitButton] =
|
||||
GetPresentationResource(Constants.ResourceNames.SubmitButtonLabel, lcid),
|
||||
[Constants.DynamicContentLabels.markerLoginPagePasswordLabel] = string.Empty
|
||||
};
|
||||
|
||||
if (_ex != null)
|
||||
{
|
||||
dynamicContents[Constants.DynamicContentLabels.markerPageIntroductionText] = GetPresentationResource(Constants.ResourceNames.FailedLogin, lcid);
|
||||
}
|
||||
|
||||
dynamicContents[Constants.DynamicContentLabels.markerLoginPageUserNameExample] = _username;
|
||||
|
||||
string authPageTemplate = ResourceHandler.GetResource(Constants.ResourceNames.AuthPageTemplate, lcid);
|
||||
|
||||
return Replace(authPageTemplate, dynamicContents);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IAdapterPresentationIndirect Members
|
||||
|
||||
public string GetFormPreRenderHtml(int lcid)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetPageTitle(int lcid)
|
||||
{
|
||||
return GetPresentationResource(Constants.ResourceNames.PageTitle, lcid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected string GetPresentationResource(string resourceName, int lcid)
|
||||
{
|
||||
return ResourceHandler.GetResource(resourceName, lcid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{CFB51980-393E-4212-BC77-9C266978BCF8}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>UsernamePasswordSecondFactor</RootNamespace>
|
||||
<AssemblyName>UsernamePasswordSecondFactor</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DelaySign>false</DelaySign>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>
|
||||
</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.IdentityServer.Web">
|
||||
<HintPath>..\..\..\..\..\Desktop\ADFSBinaries\Microsoft.IdentityServer.Web.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.DirectoryServices.AccountManagement" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="PasswordValidator.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ResourceHandler.cs" />
|
||||
<Compile Include="Resources\StringResources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>StringResources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UsernamePasswordAdapter.cs" />
|
||||
<Compile Include="UsernamePasswordException.cs" />
|
||||
<Compile Include="UsernamePasswordMetadata.cs" />
|
||||
<Compile Include="UsernamePasswordPresentation.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\StringResources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>StringResources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
Загрузка…
Ссылка в новой задаче