With cURL, we can pass a username with an HTTP web request as follows:
$ curl -u <your_username> https://api.github.com/user
The -u
flag accepts a username for authentication, and then cURL will request the password. The cURL example is for Basic authentication with the GitHub Api.
How do we similarly pass a username and password along with Invoke-WebRequest? The ultimate goal is to user PowerShell with Basic authentication in the GitHub API.
$pair = "$($user):$($pass)"
Check the approved answer. I was using the above and it gave me too much pain
-Credential
approach work as the correct auth header is not generated when the request is made.
I am assuming Basic authentication here.
$cred = Get-Credential
Invoke-WebRequest -Uri 'https://whatever' -Credential $cred
You can get your credential through other means (Import-Clixml
, etc.), but it does have to be a [PSCredential]
object.
Edit based on comments:
GitHub is breaking RFC as they explain in the link you provided:
The API supports Basic Authentication as defined in RFC2617 with a few slight differences. The main difference is that the RFC requires unauthenticated requests to be answered with 401 Unauthorized responses. In many places, this would disclose the existence of user data. Instead, the GitHub API responds with 404 Not Found. This may cause problems for HTTP libraries that assume a 401 Unauthorized response. The solution is to manually craft the Authorization header.
Powershell's Invoke-WebRequest
does to my knowledge wait for a 401 response before sending the credentials, and since GitHub never provides one, your credentials will never be sent.
Manually build the headers
Instead you'll have to create the basic auth headers yourself.
Basic authentication takes a string that consists of the username and password separated by a colon user:pass
and then sends the Base64 encoded result of that.
Code like this should work:
$user = 'user'
$pass = 'pass'
$pair = "$($user):$($pass)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
$Headers = @{
Authorization = $basicAuthValue
}
Invoke-WebRequest -Uri 'https://whatever' -Headers $Headers
You could combine some of the string concatenation but I wanted to break it out to make it clearer.
Use this:
$root = 'REST_SERVICE_URL'
$user = "user"
$pass= "password"
$secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
$result = Invoke-RestMethod $root -Credential $credential
If someone would need a one liner:
iwr -Uri 'https://api.github.com/user' -Headers @{ Authorization = "Basic "+ [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("user:pass")) }
Invoke-WebRequest
follows the RFC2617 as @briantist noted, however there are some systems (e.g. JFrog Artifactory) that allow anonymous usage if the Authorization
header is absent, but will respond with 401 Forbidden
if the header contains invalid credentials.
This can be used to trigger the 401 Forbidden
response and get -Credentials
to work.
$login = Get-Credential -Message "Enter Credentials for Artifactory"
#Basic foo:bar
$headers = @{ Authorization = "Basic Zm9vOmJhcg==" }
Invoke-WebRequest -Credential $login -Headers $headers -Uri "..."
This will send the invalid header the first time, which will be replaced with the valid credentials in the second request since -Credentials
overrides the Authorization
header.
Tested with Powershell 5.1
credential
where user and password is given? According to your answer, -credential
will only work if it gets a 401
from headers
before and therefore there must be basic foo:bar
so that it is not anonymous ?
Authorization
header trigger the 401
to then get -Credential
to work.
foo:bar
is correct, I need to manipulate it to .e.g. foobar:bar
for the auth header key
..
foo:bar
, then you need to use something different.
I had to do this to get it to work:
$pair = "$($user):$($pass)"
$encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($Pair))
$headers = @{ Authorization = "Basic $encodedCredentials" }
Invoke-WebRequest -Uri $url -Method Get -Headers $headers -OutFile Config.html
Here is another way using WebRequest, I hope it will work for you
$user = 'whatever'
$pass = 'whatever'
$secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
$headers = @{ Authorization = "Basic Zm9vOmJhcg==" }
Invoke-WebRequest -Credential $credential -Headers $headers -Uri "https://dc01.test.local/"
-Headers
parameter expects a dictionary or hashtable of [string],[string]
. You can actually just define an array - e.g. @(Authorization = 'Basic ...')
like he did here an powershell will cast it for you
basic
with digest
which is used in the answer with the credential
parameter.
This is what worked for our particular situation.
Notes are from Wikipedia on Basic Auth from the Client Side. Thank you to @briantist's answer for the help!
Combine the username and password into a single string username:password
$user = "shaunluttin"
$pass = "super-strong-alpha-numeric-symbolic-long-password"
$pair = "${user}:${pass}"
Encode the string to the RFC2045-MIME variant of Base64, except not limited to 76 char/line.
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
Create the Auth value as the method, a space, and then the encoded pair Method Base64String
$basicAuthValue = "Basic $base64"
Create the header Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
$headers = @{ Authorization = $basicAuthValue }
Invoke the web-request
Invoke-WebRequest -uri "https://api.github.com/user" -Headers $headers
The PowerShell version of this is more verbose than the cURL version is. Why is that? @briantist pointed out that GitHub is breaking the RFC and PowerShell is sticking to it. Does that mean that cURL is also breaking with the standard?
another way is to use certutil.exe save your username and password in a file e.g. in.txt as username:password
certutil -encode in.txt out.txt
Now you should be able to use auth value from out.txt
$headers = @{ Authorization = "Basic $((get-content out.txt)[1])" }
Invoke-WebRequest -Uri 'https://whatever' -Headers $Headers
I know this is a little off the OPs original request but I came across this while looking for a way to use Invoke-WebRequest against a site requiring basic authentication.
The difference is, I did not want to record the password in the script. Instead, I wanted to prompt the script runner for credentials for the site.
Here's how I handled it
$creds = Get-Credential
$basicCreds = [pscredential]::new($Creds.UserName,$Creds.Password)
Invoke-WebRequest -Uri $URL -Credential $basicCreds
The result is the script runner is prompted with a login dialog for the U/P then, Invoke-WebRequest is able to access the site with those credentials. This works because $Creds.Password is already an encrypted string.
I hope this helps someone looking for a similar solution to the above question but without saving the username or PW in the script
Success story sharing