Skip to content

Commit 7c9bddf

Browse files
markekrausTravisEz13
authored andcommitted
Add Authentication Parameter to Web Cmdlets for Basic and OAuth (PowerShell#5052)
Closes PowerShell#4274 Adds an -Authentication parameter to Invoke-RestMethod and Invoke-WebRequest Adds an -Token parameter to Invoke-RestMethod and Invoke-WebRequest Adds an -AllowUnencryptedAuthentication parameter to Invoke-RestMethod and Invoke-WebRequest Adds tests for various -Authorization uses -Authentication Parameter has 3 options: Basic, OAuth, and Bearer Basic requires -Credential and provides RFC-7617 Basic Authorization credentials to the remote server OAuth and Bearer require the -Token which is a SecureString containing the bearer token to send to the remote server If any authentication is provided for any transport scheme other than HTTPS, the request will result in an error. A user may use the -AllowUnencryptedAuthentication switch to bypass this behavior and send their secrets unencrypted at their own risk. -Authentication does not work with -UseDefaultCredentials and will result in an error. The existing behavior with -Credential is left untouched. When not supplying -Authentication, A user will not receive an error when using -Credential over unencrypted connections. Code design choice is meant to accommodate more Authentication types in the future. Documentation Needed The 3 new parameters will need to be added to the Invoke-RestMethod and Invoke-WebRequest documentation along with examples. Syntax will need to be updated.
1 parent 2cc0911 commit 7c9bddf

3 files changed

Lines changed: 344 additions & 1 deletion

File tree

src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Text;
1111
using System.Collections;
1212
using System.Globalization;
13+
using System.Security;
1314
using System.Security.Cryptography;
1415
using System.Security.Cryptography.X509Certificates;
1516
#if !CORECLR
@@ -19,6 +20,32 @@
1920

2021
namespace Microsoft.PowerShell.Commands
2122
{
23+
/// <summary>
24+
/// The valid values for the -Authentication parameter for Invoke-RestMethod and Invoke-WebRequest
25+
/// </summary>
26+
public enum WebAuthenticationType
27+
{
28+
/// <summary>
29+
/// No authentication. Default.
30+
/// </summary>
31+
None,
32+
33+
/// <summary>
34+
/// RFC-7617 Basic Authentication. Requires -Credential
35+
/// </summary>
36+
Basic,
37+
38+
/// <summary>
39+
/// RFC-6750 OAuth 2.0 Bearer Authentication. Requires -Token
40+
/// </summary>
41+
Bearer,
42+
43+
/// <summary>
44+
/// RFC-6750 OAuth 2.0 Bearer Authentication. Requires -Token
45+
/// </summary>
46+
OAuth,
47+
}
48+
2249
/// <summary>
2350
/// Base class for Invoke-RestMethod and Invoke-WebRequest commands.
2451
/// </summary>
@@ -61,6 +88,22 @@ public abstract partial class WebRequestPSCmdlet : PSCmdlet
6188

6289
#region Authorization and Credentials
6390

91+
/// <summary>
92+
/// Gets or sets the AllowUnencryptedAuthentication property
93+
/// </summary>
94+
[Parameter]
95+
public virtual SwitchParameter AllowUnencryptedAuthentication { get; set; }
96+
97+
/// <summary>
98+
/// Gets or sets the Authentication property used to determin the Authentication method for the web session.
99+
/// Authentication does not work with UseDefaultCredentials.
100+
/// Authentication over unencrypted sessions requires AllowUnencryptedAuthentication.
101+
/// Basic: Requires Credential
102+
/// OAuth/Bearer: Requires Token
103+
/// </summary>
104+
[Parameter]
105+
public virtual WebAuthenticationType Authentication { get; set; } = WebAuthenticationType.None;
106+
64107
/// <summary>
65108
/// gets or sets the Credential property
66109
/// </summary>
@@ -94,6 +137,12 @@ public abstract partial class WebRequestPSCmdlet : PSCmdlet
94137
[Parameter]
95138
public virtual SwitchParameter SkipCertificateCheck { get; set; }
96139

140+
/// <summary>
141+
/// Gets or sets the Token property. Token is required by Authentication OAuth and Bearer.
142+
/// </summary>
143+
[Parameter]
144+
public virtual SecureString Token { get; set; }
145+
97146
#endregion
98147

99148
#region Headers
@@ -274,6 +323,38 @@ internal virtual void ValidateParameters()
274323
ThrowTerminatingError(error);
275324
}
276325

326+
// Authentication
327+
if (UseDefaultCredentials && (Authentication != WebAuthenticationType.None))
328+
{
329+
ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationConflict,
330+
"WebCmdletAuthenticationConflictException");
331+
ThrowTerminatingError(error);
332+
}
333+
if ((Authentication != WebAuthenticationType.None) && (null != Token) && (null != Credential))
334+
{
335+
ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationTokenConflict,
336+
"WebCmdletAuthenticationTokenConflictException");
337+
ThrowTerminatingError(error);
338+
}
339+
if ((Authentication == WebAuthenticationType.Basic) && (null == Credential))
340+
{
341+
ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationCredentialNotSupplied,
342+
"WebCmdletAuthenticationCredentialNotSuppliedException");
343+
ThrowTerminatingError(error);
344+
}
345+
if ((Authentication == WebAuthenticationType.OAuth || Authentication == WebAuthenticationType.Bearer) && (null == Token))
346+
{
347+
ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationTokenNotSupplied,
348+
"WebCmdletAuthenticationTokenNotSuppliedException");
349+
ThrowTerminatingError(error);
350+
}
351+
if (!AllowUnencryptedAuthentication && (Authentication != WebAuthenticationType.None) && (Uri.Scheme != "https"))
352+
{
353+
ErrorRecord error = GetValidationError(WebCmdletStrings.AllowUnencryptedAuthenticationRequired,
354+
"WebCmdletAllowUnencryptedAuthenticationRequiredException");
355+
ThrowTerminatingError(error);
356+
}
357+
277358
// credentials
278359
if (UseDefaultCredentials && (null != Credential))
279360
{
@@ -389,7 +470,7 @@ internal virtual void PrepareSession()
389470
//
390471
// handle credentials
391472
//
392-
if (null != Credential)
473+
if (null != Credential && Authentication == WebAuthenticationType.None)
393474
{
394475
// get the relevant NetworkCredential
395476
NetworkCredential netCred = Credential.GetNetworkCredential();
@@ -398,6 +479,10 @@ internal virtual void PrepareSession()
398479
// supplying a credential overrides the UseDefaultCredentials setting
399480
WebSession.UseDefaultCredentials = false;
400481
}
482+
else if ((null != Credential || null!= Token) && Authentication != WebAuthenticationType.None)
483+
{
484+
ProcessAuthentication();
485+
}
401486
else if (UseDefaultCredentials)
402487
{
403488
WebSession.UseDefaultCredentials = true;
@@ -666,6 +751,34 @@ private bool IsCustomMethodSet()
666751
return (ParameterSetName == "CustomMethod");
667752
}
668753

754+
private string GetBasicAuthorizationHeader()
755+
{
756+
string unencoded = String.Format("{0}:{1}", Credential.UserName, Credential.GetNetworkCredential().Password);
757+
Byte[] bytes = Encoding.UTF8.GetBytes(unencoded);
758+
return String.Format("Basic {0}", Convert.ToBase64String(bytes));
759+
}
760+
761+
private string GetBearerAuthorizationHeader()
762+
{
763+
return String.Format("Bearer {0}", new NetworkCredential(String.Empty, Token).Password);
764+
}
765+
766+
private void ProcessAuthentication()
767+
{
768+
if(Authentication == WebAuthenticationType.Basic)
769+
{
770+
WebSession.Headers["Authorization"] = GetBasicAuthorizationHeader();
771+
}
772+
else if (Authentication == WebAuthenticationType.Bearer || Authentication == WebAuthenticationType.OAuth)
773+
{
774+
WebSession.Headers["Authorization"] = GetBearerAuthorizationHeader();
775+
}
776+
else
777+
{
778+
Diagnostics.Assert(false, String.Format("Unrecognized Authentication value: {0}", Authentication));
779+
}
780+
}
781+
669782
#endregion Helper Methods
670783
}
671784
}

src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,21 @@
120120
<data name="AccessDenied" xml:space="preserve">
121121
<value>Access to the path '{0}' is denied.</value>
122122
</data>
123+
<data name="AllowUnencryptedAuthenticationRequired" xml:space="preserve">
124+
<value>The cmdlet cannot protect plain text secrets sent over unencrypted connections. To supress this warning and send plain text secrets over unencrypted networks, reissue the command specifying the AllowUnencryptedAuthentication parameter.</value>
125+
</data>
126+
<data name="AuthenticationConflict" xml:space="preserve">
127+
<value>The cmdlet cannot run because the following conflicting parameters are specified: Authentication and UseDefaultCredentials. Authentication does not support Default Credentials. Specify either Authentication or UseDefaultCredentials, then retry.</value>
128+
</data>
129+
<data name="AuthenticationCredentialNotSupplied" xml:space="preserve">
130+
<value>The cmdlet cannot run because the following parameter is not specified: Credential. The supplied Authentication type requires a Credential. Specify Credential, then retry.</value>
131+
</data>
132+
<data name="AuthenticationTokenNotSupplied" xml:space="preserve">
133+
<value>The cmdlet cannot run because the following parameter is not specified: Token. The supplied Authentication type requires a Token. Specify Token, then retry.</value>
134+
</data>
135+
<data name="AuthenticationTokenConflict" xml:space="preserve">
136+
<value>The cmdlet cannot run because the following conflicting parameters are specified: Credential and Token. Specify either Credential or Token, then retry.</value>
137+
</data>
123138
<data name="BodyConflict" xml:space="preserve">
124139
<value>The cmdlet cannot run because the following conflicting parameters are specified: Body and InFile. Specify either Body or Infile, then retry. </value>
125140
</data>

0 commit comments

Comments
 (0)