Generate a JWT with an X.509 certificate signature in Powershell - powershell

I'm trying to get an API access token from Active Directory using certificate authentication as shown here:
Access token request with a certificate
The request needs the client_assertion property, which is a JWT created from the certificate with the format and specs mentioned here:
Assertion format
Is there a way of generating this token in Powershell that is non-interactive as this is part of a release pipeline?
Edit: To make it a little more clear, this C# code using the Microsoft.IdentityModel.Clients.ActiveDirectory library is what I'm trying to do in Powershell, specifically the second line:
AuthenticationContext authContext = new AuthenticationContext(authority);
IClientAssertionCertificate assertion = new ClientAssertionCertificate(clientId, certificate);
authenticationResult = await authContext.AcquireTokenAsync(resource, assertion);

Sample code (just tested this, this works for me when I register an app as a web app).
<#
Sample to connect to Graph using a certificate to authenticate
Prerequisite : ADAL (Microsoft.IdentityModel.Clients.ActiveDirectory.dll)
#>
# Load the ADAL Assembly
Add-Type -Path "E:\Assemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.4.3.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
# Settings for the application
$AppID = '<ID OF THE WEB APP>'
$TenantDomain = '<TENANT>'
$LoginUri = 'https://login.microsoftonline.com/'
$Resource = 'https://graph.microsoft.com'
$Certificate = Get-Item 'Cert:\CurrentUser\My\<CERTIFICATE THUMBPRINT>' # This points to my own certificate
# Auth Authority Uri
$Authority = "$LoginUri/$TenantDomain"
# Create the authenticationContext
$Context = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($Authority)
# create the CAC
$CAC = [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate]::new($AppID,$Certificate)
# Get the token
$TokenResponse = $Context.AcquireTokenAsync($Resource,$CAC)
Start-Sleep -Seconds 1 # Sleep for 1 second...
# Token should be present
$TokenResult = $TokenResponse.Result

As I know, you could not use PowerShell to create the JWT.
You could use jwt.io to edit each part (header, payload), and then jwt.io will automatically encode it into a JWT for the client_assertion.

Related

Powershell to Update Google Sheets

I have imported a module UMN-Google from https://github.com/umn-microsoft-automation/UMN-Google in PowerShell which is used to create, update Google Sheets.
I create a Project in https://console.cloud.google.com/
Enabled the Google Sheets and Drive API.
Create a Service Account and generated a private key(P12 format).
Following is the PowerShell Code to generate Access Token and same access token is used to create or update the sheet which is working perfectly.
Import-Module UMN-Google
# Set security protocol to TLS 1.2 to avoid TLS errors
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Google API Authozation
$scope = "https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file"
$certPath = "C:\Temp\sheets-script-336006-5db4e6b09111.p12"
$iss = 'sheets-script#sheets-script-336006.iam.gserviceaccount.com'
$certPswd = 'notasecret'
try {
$accessToken = Get-GOAuthTokenService -scope $scope -certPath $certPath -certPswd $certPswd -iss $iss
} catch {
$err = $_.Exception
$err | Select-Object -Property *
"Response: "
$err.Response
}
$accessToken
# Create new spreadsheet
$Title = 'Patching Spreadsheet'
$SpreadsheetID = (New-GSheetSpreadSheet -accessToken $accessToken -title $Title).spreadsheetId
$SpreadsheetID
# Create new sheet
$Sheet = 'Computers'
Add-GSheetSheet -accessToken $accessToken -sheetName $Sheet -spreadSheetID $SpreadsheetID
Now I have many google sheets to which I want the script to be integrated in-order to update the data in various sheets. We have disabled drive sharing outside domain due to which I am unable to give permission to service account for sheets editing.
Is there any way or possibility which can be done so that the service account has the permission to update the sheets.
This is related to something that I have run into recently, specifically with having a service account "impersonate" a user within our Google Workspace in order to perform calls to Google Drive API.
Our solution came from this guide here: Delegating domain-wide authority to the service account.
The idea is to have your service account assume the role of an account in your Google Workspace that does have the permissions you need, in this case to edit specific Sheets. The instructions are pretty straightforward in that guide; just a quick setup in the Admin console.
It's worth noting that we are not using Get-GOAuthTokenService to get an access token. We are building a JWT using Svyatoslav Pidgorny's JWT module here and using it to get an access token via the https://oauth2.googleapis.com/token endpoint.
I only took a quick peek at UMN-Google's implementation, but I see that their JWT-creation doesn't offer any sub parameter in the JWT payload. This is the parameter that let's you designate which Workspace user that you want the service account to impersonate.

Authenticate via OAuth with App Secret to EWS in PowerShell

I am seeking the proper PowerShell code for how to authenticate to EWS via OAuth with only an app secret instead of a username and password. I have the app registration set with full_access_as_app permissions. The use case is the application runs is a daemon that sends emails based on supplied to and from addresses. The from users are remote users do not authenticate into the system so process that handles sending the emails cannot authenticate as them via OAuth.
I found this it was helpful so I assume only the part about getting the token needs to be changed: Powershell, EWS, OAuth2, and automation
There are a few different approaches you could take eg if you used the MSAL library which is different from ADAL which the script you pointed is using then you could do something like.
$ClientId = "9d5d77a6-xxxx-473e-8931-958f15f1a96b"
$MailboxName = "gscales#domain.com"
$RedirectUri = "msal9d5d77a6-fe09-473e-8931-958f15f1a96b://auth"
$ClientSecret = "xxx";
$Scope = "https://outlook.office365.com/.default"
$TenantId = (Invoke-WebRequest https://login.windows.net/datarumble.com/v2.0/.well-known/openid-configuration | ConvertFrom-Json).token_endpoint.Split('/')[3]
$app = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($ClientId).WithClientSecret($ClientSecret).WithTenantId($TenantId).WithRedirectUri($RedirectUri).Build()
$Scopes = New-Object System.Collections.Generic.List[string]
$Scopes.Add($Scope)
$TokenResult = $app.AcquireTokenForClient($Scopes).ExecuteAsync().Result;

Acquire Authorization Token for Azure Function App from PowerShell

I've a problem getting token for my azure application
here is the code
function Get-Token
{
ipmo "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
ipmo "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll"
$clientId = "1b730954-1685-4b74-9bfd-dac224a7b894"
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
$resourceAppIdURI = "https://ios111.azurewebsites.net/"
$authority = "https://login.microsoftonline.com/common/"
$authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]$authority
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $redirectUri, "Auto")
$authResult.CreateAuthorizationHeader()
}
I receive the following error
Exception calling "AcquireToken" with "4" argument(s): "AADSTS65005:
Invalid resource. The client has requested access to a resource which
is not listed in the requested permissions in the client's application
registration. Client app ID: 1b730954-1685-4b74-9bfd-dac224a7b894.
Resource value from request: https://ios111.azurewebsites.net/.
Resource app ID: f4c1cc8d-629a-4c7e-836a-120ff078e664. List of valid
resources from app registration: .
However if i change the $resourceAppIdURI to
$resourceAppIdURI = "https://management.core.windows.net/"
It's all ok, and i'm authorized to access my application with received token (if i set Authorization header value to this token), but without roles claim which i define in application manifest for this user and which i want to check.
If i just access my function from browser, after login page redirected me back to a function, there is no a Authorization header specified by browser but ARRAffinity cookie and ClaimsPrincipal.Current.Claims in function context has correct roles claim. So, seems in case of PS, there JWT token acquired by .AcquireToken is deserialized to ClaimsPrincipal.Current without using internal web app logic.
Any ideas how to give PS client a permission to access my app ?
Thanks !
To Get Azuere App Token with the required roles, you need a ClientId and Secret, along with required permissions setup, if admin-consent is needed, you should click the 'Grant Permissions' button on the application properites in the Azure Portal.
Then, if all is set correct, you can get a token like this (with the roles included):
Example for the Microsoft Graph API
$adal = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
[System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
$TenantName = "tenant.onmicrosoft.com"
$ClientId = "d1245516-2bg3-1234-123d-7cd067ff66b4" # Your AppId (Just a sample)
$Secret = "H7dd+PejUddGhuuGYY234Xhhhjs7739iQn112317zg=" # Your App Key Secret (Just a sample)
$AuthId = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential($clientId,$secret)
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
$resourceAppIdURI = "https://graph.microsoft.com"
[uri]$authority = "https://login.windows.net/$TenantName/oauth2/authorize"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$Token = $authResult = $authContext.AcquireToken($resourceAppIdURI, $AuthId)
To Check the token you can use this JWT Token Decoder, to see if it has the required roles:
http://jwt.calebb.net/
Solved !
Thank for the help you guys !
Actually, i had to register native azure application, give it access for my WebApp and use this appId as the clientId in the script above. Using "1b730954-1685-4b74-9bfd-dac224a7b894" as Well Known PowerShell clientId probably possible for standard MS app, but there no way you can grand PS access for you app, at least not from azure portal.
Here is the link https://markscholman.com/2016/08/consuming-azure-api-app-azure-ad-authentication-using-powershell/ with step by step explanation given by WayneYang-MSFT

How can I authenticate to AAD and call the Graph API as a Daemon Application with PowerShell?

I am trying to do some very quick tests on Azure Active Directory, and I want to use a Daemon Application to access the Graph API without needing a user present to authenticate. I want to verify that my application registration can successfully authenticate to AAD, that my client secret is valid, and make calls to the AAD Graph API.
I have registered a "Web App/API" in my directory already, and I have set it up to have the appropriate permissions to call the AAD Graph API in the App Only Context. I have also generated an application key/certificate for my app so that I can authenticate as a confidential client.
I want to take a look at my AAD Token, and the output from the Graph API after my call. How can I use PowerShell to quickly accomplish this?
This question is very similar to this one where create a PowerShell script to authenticate as a Native Client Application. However, in this situation, there are some subtle and important differences because you want to authenticate as a confidential client. Specifically, we need to create a Client Credential so that we can authenticate without a user as a Daemon Application.
First you need to download and save the .NET dlls for ADAL. The download link can be found on Nuget.
Note: We specifically use ADAL v2 here.
You can extract the contents of the .nupkg with a File Extractor like
7z, WinZip, etc...
Extract the contents from \lib\net45\ and copy them into your working directory. I put the files in their own "ADAL" folder, to keep it separate.
Then you should be able to create a new PowerShell script with the following:
# Load ADAL
Add-Type -Path ".\ADAL\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
# Output Token and Response from AAD Graph API
$accessToken = ".\Token.txt"
$output = ".\Output.json"
# Application and Tenant Configuration
$clientId = "<AppIDGUID>"
$tenantId = "<TenantID>"
$resourceId = "https://graph.windows.net"
$login = "https://login.microsoftonline.com"
# Create Client Credential Using App Key
$secret = "<AppKey>"
# Create Client Credential Using Certificate
#$certFile = "<PFXFilePath>"
#$certFilePassword = "<CertPassword>"
#$secret = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate -ArgumentList $certFile,$certFilePassword
# Get an Access Token with ADAL
$clientCredential = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential($clientId,$secret)
$authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("{0}/{1}" -f $login,$tenantId)
$authenticationResult = $authContext.AcquireToken($resourceId, $clientcredential)
($token = $authenticationResult.AccessToken) | Out-File $accessToken
# Call the AAD Graph API
$headers = #{
"Authorization" = ("Bearer {0}" -f $token);
"Content-Type" = "application/json";
}
Invoke-RestMethod -Method Get -Uri ("{0}/{1}/users?api-version=1.6" -f $resourceId,$tenantId) -Headers $headers -OutFile $output
Note: You will need to update the App ID, Tenant ID, and your App Secret information in this script. If you use a certificate to authenticate, simply comment out the code that uses the App Key, and un-comment the code which uses the certificate. I have also pre-configured the AAD Graph API call to return the users in my tenant, but you can change this REST call to whatever you want.
After you successfully run the script, you should get 2 new files in your working directory: A text file that contains your encoded JSON access token, which can be base64 decoded on sites like this, and a JSON file with the response from the AAD Graph API.
Let me know if this helps!

how to import files from SharePoint team site to local machines?

My client generating reports (excel files) monthly basis and they upload the files in the SharePoint document library. Again some reports are coming through the mail (outlook). 
The requirement is we need to copy the excel reports (which are in document library and the files that are coming through mail) to the Landing zone(FTP Server) and we need to automate this process every month.
Which is the best way to achieve this ? please help me on this.Thank you..
$uri = "https://intranet.company.com/departments/SampleSite/_api/web/getfilebyserverrelativeurl('/departments/SampleSite/Shared Documents/test.docx')/`$Value"
$FileBinary = $(Invoke-WebRequest -Uri $uri -Credential $cred -ContentType 'application/json;odata=verbose').Content
[System.IO.File]::WriteAllBytes('C:\tempfolder\test.docx',$FileBinary)
With this sample you can download a file and save it (it uses the SP REST API). This simple sample uses the credentials of the currently logged on user.
Edit : on request I've added a sample for SharePoint Online (you need the SP SDK for this because of the login flow). Code is not optimized though since this was one of my first samples. The problem is that you have to "hardcode" credentials. My solution for this within my company is encrypting the credentials with a certificate : I encrypt the credentials in a file using the public key of the service account that's running the script ; the service account uses the private key to decrypt it. Not ideal but safe enough for us.
$cred = Get-Credential # Get credentials
## Load the Assemblies
add-type -AssemblyName system.net.http
Add-type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SharePoint.Client.Runtime\v4.0_16.0.0.0__71e9bce111e9429c\Microsoft.SharePoint.Client.Runtime.dll"
# Put credentials in SPO credential object
$SPCred = [Microsoft.SharePoint.Client.SharePointOnlineCredentials]::new($cred.UserName,$cred.Password)
# Set the uri's
$uri = [uri]::new('https://YourTentant.sharepoint.com')
$fullUri = "https://YourTentant.sharepoint.com/sites/ApiTest/_api/web/getfilebyserverrelativeurl('/sites/ApiTest/Shared Documents/Document.docx')/`$Value"
# Create HTTPClientHandler object and set the properties
$handler = [System.Net.Http.HttpClientHandler]::new()
$handler.Credentials = $SPCred
$handler.CookieContainer.SetCookies($uri,$SPCred.GetAuthenticationCookie($uri))
# Create the client and set the options
$Client = [System.Net.Http.HttpClient]::new($handler)
$Client.DefaultRequestHeaders.Accept.Clear()
$Client.DefaultRequestHeaders.Accept.Add([System.Net.Http.Headers.MediaTypeWithQualityHeaderValue]::new('application/json'))
# Call the URL (async) and get the response
$Response = $Client.GetAsync($fullUri)
$data = $Response.Result
## now download the data as string (in the final version I'm going to call the ReadAsStream() method instead to get the binary file)
$a = $data.Content.ReadAsStringAsync()
$a.Result