src/auth/V1AuthProvider.ps1
# Copyright 2021, Adam Edwards # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. . (import-script AuthProvider) ScriptClass V1AuthProvider { $base = $null function __initialize( $base ) { $this.base = $base } function GetAuthContext($app, $authUri, $groupId, [securestring] $certificatePassword) { New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authUri, $this.scriptclass.__TokenCache } function GetUserInformation($token) { $userId = $null $scopes = $null $userObjectId = $null if ( $token -and $token.UserInfo ) { $userId = $token.UserInfo.DisplayableId $scopes = $null $userObjectId = $token.UserInfo.UniqueId } [PSCustomObject]@{ userId = $userId scopes = $scopes userObjectId = $userObjectId } } function AcquireFirstUserToken($authContext, $scopes) { write-verbose 'V1 auth provider acquiring initial user token' $promptBehaviorValue = ([Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::SelectAccount) $promptBehavior = new-object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList $promptBehaviorValue $authContext.protocolContext.AcquireTokenAsync( $authContext.GraphEndpointUri, $authContext.App.AppId, $authContext.App.RedirectUri, $promptBehavior) } function AcquireFirstUserTokenFromDeviceCode($authContext, $scopes) { throw [NotImplementedException]::new("Device code authentication is not implemented for the v1 authentication protocol.") } function AcquireFirstAppToken($authContext, [securestring] $certificatePassword) { write-verbose 'V1 auth provider acquiring initial app token' __AcquireAppToken $authContext $certificatePassword } function AcquireFirstUserTokenConfidential($authContext, $scopes) { throw [NotImplementedException]::new("Confidential delegated user authentication not yet implemented for v1 authentication provider") } function AcquireRefreshedToken($authContext, $token) { write-verbose 'V1 auth provider refreshing existing token' # The token is irrelevant for v1 auth, since scopes are # static and defined per app in v1. So for app-only auth, # the token cache can just use the app id as a key, and # for user auth, app+user is the key -- if the auth context # has a token cache, that's all you need to look up the # previously used token if ( $authContext.app.authtype -eq ([GraphAppAuthType]::AppOnly) ) { __AcquireAppToken $authContext } else { $userId = new-object Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier -ArgumentList $token.userinfo.uniqueid, ([Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifierType]::UniqueId) $authContext.protocolContext.AcquireTokenSilentAsync( $authContext.GraphEndpointUri, $authContext.App.AppId, $userId) } } function ClearToken($authContext, $token) { write-verbose 'V1 auth provider clearing existing token' $userUpn = if ($token.userinfo) { $token.userinfo.displayableid } write-verbose "Clearing token for user '$userUpn'" $tokenAsCacheItem = $this.scriptclass.__tokencache.ReadItems() | where { $_.accesstoken -eq $token.accesstoken } if ( $tokenAsCacheItem ) { write-verbose "Found cached token, clearing..." $this.scriptclass.__TokenCache.DeleteItem($tokenAsCacheItem) write-verbose "Successfully cleared token from the cache" } else { write-verbose "Unable to find cached token, skipping unnecessary removal from cache" } } function __AcquireAppToken($authContext, [securestring] $certificatePassword) { write-verbose 'V1 auth provider acquiring app token' $clientCredential = switch ( $authContext.app.secret.type ) { ([SecretType]::Certificate) { write-verbose 'V1 auth provider accessing specified certificate as client credential' $clientCertificate = $authContext.app.secret.GetSecretData($certificatePassword) [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate]::new($authContext.App.AppId, $clientCertificate) } ([SecretType]::Password) { write-verbose 'V1 auth provider accessing symmetric key secret as client credential' $clientSecret = [Microsoft.IdentityModel.Clients.ActiveDirectory.SecureClientSecret]::new($authcontext.app.secret.data) [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential]::new($authContext.App.AppId, $clientSecret) } default { throw [ArgumentException]::("V1 auth does not support secret type '{0}'" -f $this.tostring()) } } $authContext.protocolContext.AcquireTokenAsync( $authContext.GraphEndpointUri, $clientCredential) } static { $__AuthLibraryLoaded = $false $__TokenCache = $null $scriptRoot = $null function __initialize($scriptRoot) { $this.scriptRoot = $scriptRoot } function InitializeProvider { if ( ! $this.__AuthLibraryLoaded ) { $libPath = join-path $this.scriptRoot ../../lib Import-Assembly Microsoft.IdentityModel.Clients.ActiveDirectory -AssemblyRoot $libPath | out-null $this.__AuthLibraryLoaded = $true } if ( ! $this.__TokenCache ) { $this.__TokenCache = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache } } function RegisterProvider { $::.AuthProvider |=> RegisterProvider ([GraphAuthProtocol]::v1) $this } } } $::.V1AuthProvider |=> __initialize $psscriptroot |