Skip to content

Commit

Permalink
Add PoP token support for ROPC flow (#412)
Browse files Browse the repository at this point in the history
* Add PoP token support for ROPC flow

Add VCR tests for ROPC flow

Fix formatting issues

Add VCR tests for ROPC flow

* Vendor msal release v1.2.2

* Redact username + tenantID, fix failing test

* Split msal into confidential+public, add tests

* Fix typo

* Add tests for getPublicClient

* Make tokenWithOptions private to token package

---------

Co-authored-by: rharpavat <rharpavat@CPC-rharp-FHSOH>
  • Loading branch information
rharpavat and rharpavat authored Feb 23, 2024
1 parent a6824d8 commit 2d6ac56
Show file tree
Hide file tree
Showing 16 changed files with 1,876 additions and 58 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1
github.com/Azure/go-autorest/autorest v0.11.29
github.com/Azure/go-autorest/autorest/adal v0.9.23
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/go-cmp v0.6.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand Down
50 changes: 0 additions & 50 deletions pkg/internal/pop/msal.go → pkg/internal/pop/msal_confidential.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,8 @@ import (

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
)

// AcquirePoPTokenInteractive acquires a PoP token using MSAL's interactive login flow.
// Requires user to authenticate via browser
func AcquirePoPTokenInteractive(
context context.Context,
popClaims map[string]string,
scopes []string,
authority,
clientID string,
options *azcore.ClientOptions,
) (string, int64, error) {
var client public.Client
var err error
if options != nil && options.Transport != nil {
client, err = public.New(
clientID,
public.WithAuthority(authority),
public.WithHTTPClient(options.Transport.(*http.Client)),
)
} else {
client, err = public.New(
clientID,
public.WithAuthority(authority),
)
}
if err != nil {
return "", -1, fmt.Errorf("unable to create public client: %w", err)
}

popKey, err := GetSwPoPKey()
if err != nil {
return "", -1, err
}
result, err := client.AcquireTokenInteractive(
context,
scopes,
public.WithAuthenticationScheme(
&PoPAuthenticationScheme{
Host: popClaims["u"],
PoPKey: popKey,
},
),
)
if err != nil {
return "", -1, fmt.Errorf("failed to create PoP token with interactive flow: %w", err)
}

return result.AccessToken, result.ExpiresOn.Unix(), nil
}

// AcquirePoPTokenConfidential acquires a PoP token using MSAL's confidential login flow.
// This flow does not require user interaction as the credentials for the request have
// already been provided
Expand Down
File renamed without changes.
115 changes: 115 additions & 0 deletions pkg/internal/pop/msal_public.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package pop

import (
"context"
"fmt"
"net/http"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
)

// AcquirePoPTokenInteractive acquires a PoP token using MSAL's interactive login flow.
// Requires user to authenticate via browser
func AcquirePoPTokenInteractive(
context context.Context,
popClaims map[string]string,
scopes []string,
authority,
clientID string,
options *azcore.ClientOptions,
) (string, int64, error) {
var client *public.Client
var err error
client, err = getPublicClient(authority, clientID, options)
if err != nil {
return "", -1, err
}

popKey, err := GetSwPoPKey()
if err != nil {
return "", -1, err
}
result, err := client.AcquireTokenInteractive(
context,
scopes,
public.WithAuthenticationScheme(
&PoPAuthenticationScheme{
Host: popClaims["u"],
PoPKey: popKey,
},
),
)
if err != nil {
return "", -1, fmt.Errorf("failed to create PoP token with interactive flow: %w", err)
}

return result.AccessToken, result.ExpiresOn.Unix(), nil
}

// AcquirePoPTokenByUsernamePassword acquires a PoP token using MSAL's username/password login flow
// This flow does not require user interaction as credentials have already been provided
func AcquirePoPTokenByUsernamePassword(
context context.Context,
popClaims map[string]string,
scopes []string,
authority,
clientID,
username,
password string,
options *azcore.ClientOptions,
) (string, int64, error) {
client, err := getPublicClient(authority, clientID, options)
if err != nil {
return "", -1, err
}

popKey, err := GetSwPoPKey()
if err != nil {
return "", -1, err
}
result, err := client.AcquireTokenByUsernamePassword(
context,
scopes,
username,
password,
public.WithAuthenticationScheme(
&PoPAuthenticationScheme{
Host: popClaims["u"],
PoPKey: popKey,
},
),
)
if err != nil {
return "", -1, fmt.Errorf("failed to create PoP token with username/password flow: %w", err)
}

return result.AccessToken, result.ExpiresOn.Unix(), nil
}

// getPublicClient returns an instance of the msal `public` client based on the provided options
func getPublicClient(
authority,
clientID string,
options *azcore.ClientOptions,
) (*public.Client, error) {
var client public.Client
var err error
if options != nil && options.Transport != nil {
client, err = public.New(
clientID,
public.WithAuthority(authority),
public.WithHTTPClient(options.Transport.(*http.Client)),
)
} else {
client, err = public.New(
clientID,
public.WithAuthority(authority),
)
}
if err != nil {
return nil, fmt.Errorf("unable to create public client: %w", err)
}

return &client, nil
}
Loading

0 comments on commit 2d6ac56

Please sign in to comment.