SCRT-HQ / PSGSuite

Powershell module for Google / G Suite API calls wrapped in handy functions. Authentication is established using a service account via P12 key to negate the consent popup and allow for greater handsoff automation capabilities
https://psgsuite.io/
Apache License 2.0
235 stars 67 forks source link

Get-GSToken : 401 - Unauthorized: invalid_grant / Invalid JWT #4

Closed TLaborde closed 6 years ago

TLaborde commented 7 years ago

I'm getting this error with the version of the module from the PSGallery.

PS version:

Name                           Value                                                                                                                                                                                                                                                                        
----                           -----                                                                                                                                                                                                                                                                        
PSVersion                      5.1.15063.674                                                                                                                                                                                                                                                                
PSEdition                      Desktop                                                                                                                                                                                                                                                                      
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}                                                                                                                                                                                                                                                      
BuildVersion                   10.0.15063.674                                                                                                                                                                                                                                                               
CLRVersion                     4.0.30319.42000                                                                                                                                                                                                                                                              
WSManStackVersion              3.0                                                                                                                                                                                                                                                                          
PSRemotingProtocolVersion      2.3                                                                                                                                                                                                                                                                          
SerializationVersion           1.1.0.1  

Solution, in Get-GStoken, line 53-54, for iat and exp, we need only the first ten digits:

    exp = ("$expiryDate".Substring(0,10))
    iat = ("$createDate".Substring(0,10))
scrthq commented 7 years ago

Assuming you're using the full service account email and not the client ID for the AppEmail in your PSGSuiteConfig / Get-GSToken call? Also, are you passing a list of scopes or a single scope?

Could you send me your Get-GSToken command with sensitive parts removed so I can try and replicate? I use the module daily and haven't had an issue myself, but my typical calls are limited to a single scope for the most part.

TLaborde commented 7 years ago

Yes, I'm working in a lab, so the account has all power. I followed your documentation about the setup, and it went without a hitch (very good doc).

The code is very straightforward:

Import-Module PSGSuite
Set-PSGSuiteConfig @settings
$allgroups = Get-GSGroupList

$settings containing all the info to log in.

By default, the token will look like that:

{
    "iss":  "XXX",
    "sub":  "XXX",
    "scope":  "https://www.googleapis.com/auth/admin.directory.group",
    "aud":  "https://www.googleapis.com/oauth2/v4/token",
    "exp":  "15099322693728",
    "iat":  "15099289693728"
}

and i get:

Get-GSToken : 401 - Unauthorized: invalid_grant / Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values and use a clock with skew to account for clock differences between systems.

So i looked into the clock possible issues, but i'm synchronized with google servers (or close enough). I looked inside the google documentation for the JWT token, and every iat and exp values are in a ten digits format. By doing the change shown before, it works.

It could still be something weird on my side...

scrthq commented 6 years ago

The iat and exp values are unix timestamps, so I wouldn't want to adjust something via substring in case that timestamp grows. That's weird that it's generating 14 digit values for that, that would be the year 2448. Shaving off the 4 at the end does yield a correct timestamp based on your comment time.

I think a better way to approach that would be checking against the current time, but even then, something's not lining up. I'll see if I can try to dive in and reproduce, or at least put in some checks that build an appropriate JWT and prevent generating requests for 430 years from now.

On Sun, Nov 5, 2017 at 6:54 PM, Thomas Laborde notifications@github.com wrote:

Yes, I'm working in a lab, so the account has all power. I followed your documentation about the setup, and it went without a hitch (very good doc).

The code is very straightforward:

Import-Module PSGSuite Set-PSGSuiteConfig @settings $allgroups = Get-GSGroupList

$settings containing all the info to log in.

By default, the token will look like that:

{ "iss": "XXX", "sub": "XXX", "scope": "https://www.googleapis.com/auth/admin.directory.group", "aud": "https://www.googleapis.com/oauth2/v4/token", "exp": "15099322693728", "iat": "15099289693728" }

and i get:

Get-GSToken : 401 - Unauthorized: invalid_grant / Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values and use a clock with skew to account for clock differences between systems.

So i looked into the clock possible issues, but i'm synchronized with google servers (or close enough). I looked inside the google documentation for the JWT token, and every iat and exp values are in a ten digits format. By doing the change shown before, it works.

It could still be something weird on my side...

— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub https://github.com/scrthq/PSGSuite/issues/4#issuecomment-342020635, or mute the thread https://github.com/notifications/unsubscribe-auth/AMIo3SwqE6cMWBMZ0cfpqwb1p_LTtQTQks5szljHgaJpZM4QPMqs .

TLaborde commented 6 years ago

Alright, the issue is based on the locals:

PS > (Get-Date($now) -UFormat "%s")
1513840921,66042

PS > [decimal](Get-Date($now) -UFormat "%s")
151384092166042

so a substring would be more robust than a convert to decimal...

scrthq commented 6 years ago

working on the next major update for PSGSuite right now which resolves this by building the requests using Google's .NET SDK's instead of REST API calls with Invoke-RestMethod. PSGSuite v2 will also add a fair amount more features as well (multi domain support, client_secrets.json AND P12 key service account support, Drive file uploading, PowerShell Core support, and more)

wesleykirkland commented 6 years ago

@scrthq Any chance that the current implementation can do multi domain support with different P12 keys? The module have everything I need but I have 5+ GSuite tenants to pull data from.

scrthq commented 6 years ago

@wesleykirkland it can actually, although config management can get a bit unwieldy. PSGSuite 2.0.0 does this pretty seamlessly though and should be out soon (targetting the next couple weeks)

wesleykirkland commented 6 years ago

@scrthq Yeah I found the config management was a bit tricky. If you can provide some documentation on how to get started with V2 I'll beta test for you. Until then I'm having to use the config management trickery.

Also awesome work so far!

scrthq commented 6 years ago

@wesleykirkland thanks!!! The updated module will rely on PoshCode's Configuration module v1.2.0for configuration management, so make sure you have that installed:

Install-Module Configuration -Repository PSGallery -RequiredVersion 1.2.0 -Scope CurrentUser

Once installed, load in v2.0.0 and you can pass it an array of hashtables to build out the multi-config, setting whichever you want as default via the appropriate flag (this can be changed on the fly):

@(
    @{
        SetAsDefaultConfig     = $true
        ConfigName             = "main"
        P12KeyPath             = "C:\P12s\PSGSuite.p12"
        AppEmail               = "psgsuite@developer-shore.iam.gserviceaccount.com"
        AdminEmail             = "admin@mydomain.io"
        CustomerID             = "C021xxxxxx"
        Domain                 = "mydomain.io"
        Preference             = "CustomerID"
        ServiceAccountClientID = "98723498723489723498239"
    }
    @{
        ConfigName             = "client1"
        P12KeyPath             = "C:\P12s\PSGSuite-theirdomain.p12"
        AppEmail               = "psgsuite@theirdomain.iam.gserviceaccount.com"
        AdminEmail             = "admin@theirdomain.io"
        CustomerID             = "C092xxxxxx"
        Domain                 = "theirdomain.io"
        Preference             = "CustomerID"
        ServiceAccountClientID = "12399898494949494994949"
    }
) | ForEach-Object {
    $props = $_
    Set-PSGSuiteConfig @props
}

Once you have that done, you can switch between active configs by using Switch-PSGSuiteConfig -ConfigName $configName (or by using the domain name via Switch-PSGSuiteConfig -Domain theirdomain.io. If you want to switch the default config (the config that's loaded on module import), pass the -SetToDefault switch when switching configs.

scrthq commented 6 years ago

6 created to track progress on 2.0.0 rollout

scrthq commented 6 years ago

PSGSuite 2.0.0 released!

Wiki has been updated, 100% of functions also have comment based help as well so the documentation is at your fingertips while using the module directly.

Please check out the README for any breaking changes!

PowerShell Gallery link: https://www.powershellgallery.com/packages/PSGSuite/2.0.0

Cheers!

scrthq commented 6 years ago

@TLaborde FYI - I'm going to be adding a legacy branch to this repo and incorporating this fix there for those that do need to use v1.2.1 for email delegation support.

scrthq commented 6 years ago

@TLaborde - got this working, should fix the local issue. I'll be uploading 1.2.2 in GitHub releases within the next couple days!

Let me know if this works for you:

[string]$now = Get-Date (Get-Date).ToUniversalTime() -UFormat "%s"
$createDate = [int]$now.Split(".").Split(",")[0]
$expiryDate = [int]$now.Split(".").Split(",")[0] + 3540
TLaborde commented 6 years ago

perfect, thanks!

scrthq commented 6 years ago

Awesome! You'll find the legacy 1.2.2 zip in the releases section. Let me know if you need any help installing!

scrthq commented 6 years ago

FYI @TLaborde - delegation commands have been added back into PSGSuite in v2.4.0! Still uses the old API calls, but I managed to get the JWT creation needed for the REST API calls that Gmail Delegation does refactored to work in PowerShell 4.0-6.x+! If you haven't updated yet, you should be safe to do so now and retain the ability to use Gmail delegation functions!