Git Product home page Git Product logo

microsoft-authentication-library-for-go's Introduction

Microsoft Authentication Library (MSAL) for Go

The Microsoft Authentication Library (MSAL) for Go is part of the Microsoft identity platform for developers (formerly named Azure AD) v2.0. It allows you to sign in users or apps with Microsoft identities (Azure AD and Microsoft Accounts) and obtain tokens to call Microsoft APIs such as Microsoft Graph or your own APIs registered with the Microsoft identity platform. It is built using industry standard OAuth2 and OpenID Connect protocols.

The latest code resides in the dev branch.

Quick links:

Getting Started GoDoc Wiki Samples Support Feedback

Build Status

Go

Installation

Setting up Go

To install Go, visit this link.

Installing MSAL Go

go get -u github.com/AzureAD/microsoft-authentication-library-for-go/

Usage

Before using MSAL Go, you will need to register your application with the Microsoft identity platform.

Acquiring Tokens

Acquiring tokens with MSAL Go follows this general pattern. There might be some slight differences for other token acquisition flows. Here is a basic example:

  1. Create a client. MSAL separates public and confidential client applications, so call public.New() or confidential.New() to create the appropriate client for your application.

    • Initializing a public client:
    import "github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
    
    publicClient, err := public.New("client_id", public.WithAuthority("https://login.microsoftonline.com/your_tenant"))
    • Initializing a confidential client:
    import "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
    
    // confidential clients have a credential, such as a secret or a certificate
    cred, err := confidential.NewCredFromSecret("client_secret")
    if err != nil {
        // TODO: handle error
    }
    confidentialClient, err := confidential.New("https://login.microsoftonline.com/your_tenant", "client_id", cred)
  2. Call AcquireTokenSilent() to look for a cached token. If AcquireTokenSilent() returns an error, call another AcquireToken... method to authenticate.

    • Public clients should specify a user account, if one is available:
    // If your application previously authenticated a user, call AcquireTokenSilent with that user's account
    // to use cached authentication data. This example shows choosing an account from the cache, however this
    // isn't always necessary because the AuthResult returned by authentication methods includes user account
    // information.
    accounts, err := client.Accounts(context.TODO())
    if err != nil {
        // TODO: handle error
    }
    if len(accounts) > 0 {
        // There may be more accounts; here we assume the first one is wanted.
        // AcquireTokenSilent returns a non-nil error when it can't provide a token.
        result, err = client.AcquireTokenSilent(context.TODO(), scopes, public.WithSilentAccount(accounts[0]))
    }
    if err != nil || len(accounts) == 0 {
        // cache miss, authenticate a user with another AcquireToken* method
        result, err = client.AcquireTokenInteractive(context.TODO(), scopes)
        if err != nil {
            // TODO: handle error
        }
    }
    // TODO: save the authenticated user's account, use the access token
    userAccount := result.Account
    accessToken := result.AccessToken
    • Confidential clients can simply call AcquireTokenSilent():
    scopes := []string{"scope"}
    result, err := confidentialClient.AcquireTokenSilent(context.TODO(), scopes)
    if err != nil {
        // cache miss, authenticate with another AcquireToken... method
        result, err = confidentialClient.AcquireTokenByCredential(context.TODO(), scopes)
        if err != nil {
            // TODO: handle error
        }
    }
    accessToken := result.AccessToken

Community Help and Support

We use Stack Overflow to work with the community on supporting Azure Active Directory and its SDKs, including this one! We highly recommend you ask your questions on Stack Overflow (we're all on there!) Also browse existing issues to see if someone has had your question before. Please use the "msal" tag when asking your questions.

If you find and bug or have a feature request, please raise the issue on GitHub Issues.

Submit Feedback

We'd like your thoughts on this library. Please complete this short survey.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

Security Library

This library controls how users sign-in and access services. We recommend you always take the latest version of our library in your app when possible. We use semantic versioning so you can control the risk associated with updating your app. As an example, always downloading the latest minor version number (e.g. x.y.x) ensures you get the latest security and feature enhancements but our API surface remains the same. You can always see the latest version and release notes under the Releases tab of GitHub.

Security Reporting

If you find a security issue with our libraries or services please report it to [email protected] with as much detail as possible. Your submission may be eligible for a bounty through the Microsoft Bounty program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting this page and subscribing to Security Advisory Alerts.

Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License (the "License").

microsoft-authentication-library-for-go's People

Contributors

abhidnya13 avatar bgavrilms avatar chlowell avatar element-of-surprise avatar ellismg avatar eopeter avatar epelc avatar fadhilijuma avatar flavianmissi avatar hchittanuru3 avatar henrik-me avatar hickford avatar jennyf19 avatar jhendrixmsft avatar julienstroheker avatar keegan-caruso avatar ksyx avatar manojampalam avatar microsoft-github-operations[bot] avatar microsoftopensource avatar oxisto avatar pmaytak avatar qmuntal avatar rayluo avatar sangonzal avatar sarathys avatar shannoncantech avatar siddhijain avatar teamsmiley avatar testwill avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

microsoft-authentication-library-for-go's Issues

[Bug] Instance discovery cache is not thread safe

Where is the issue?

  • Other (please describe)

AadInstanceDiscovery uses a private global for it's cache but does not synchronize read and writes.

Expected behavior
Instance discovery cache is thread safe

Actual behavior
Instance discovery cache is not thread safe

**Other
sync.Once isn't really needed here either as the only thing it does is init a var, which can be done in it's declaration.

[Cleanup] Provision for optional arguments

Establish a pattern for handling optional arguments. This should be established up front as the design might impact public surface area.

The idea is to provide a mechanism to avoid breaking changes in the event that we want to add a new, optional parameter to an API.

  1. options struct to hold optional params
  2. variadic configuration pattern
  3. add new APIs with a suffix, e.g. WithFoo()

Option 1 and 2 are detailed here. Option 3 should be avoided.

Reduce use of unneeded pointer types within internal/

There are types within internal/ that use pointers to basic types, such as:

type Account struct {
	HomeAccountID     *string `json:"home_account_id,omitempty"`
	Environment       *string `json:"environment,omitempty"`
	Realm             *string `json:"realm,omitempty"`
	LocalAccountID    *string `json:"local_account_id,omitempty"`
	AuthorityType     *string `json:"authority_type,omitempty"`
	PreferredUsername *string `json:"username,omitempty"`
	GivenName         *string `json:"given_name,omitempty"`
	FamilyName        *string `json:"family_name,omitempty"`
	MiddleName        *string `json:"middle_name,omitempty"`
	Name              *string `json:"name,omitempty"`
	AlternativeID     *string `json:"alternative_account_id,omitempty"`
	RawClientInfo     *string `json:"client_info,omitempty"`
	additionalFields  map[string]interface{}
}

This shouldn't be necessary for json encode/decode. This creates a lot of objects on the heap that cause GC slowdown.

Not that this is never done, but usually only if you need to tell the difference between a zero value and set value. Protocol Buffer version 2 does this. However, that method has been deprecated in version 3 because of these types of issues.

This will take a non-insignificant amount of work to do and if it is agreed this should be worked on, we should avoid colliding with another PR in internal/.

Add CI build

  • Add CI build on either Azure Devops or Github actions that builds the code and runs unit tests for every pull request

Use custom JSON encoding/decoding vs manual map[string]interface{} method

The JSON encoding inside is kinda funky. This looks to be the case in order to support a unique backend API. If the backend API sends an updated message with a field we do not have, we must store that field instead of dropping it. Because we will often need to alter that sent message and send back, replacing what's on the server, we cannot silently drop the field as Go's stdlib encoder/decoder does.

But we shouldn't have to give up easy to use struct decoding for map[string]interface{}, which is inefficient and hard to follow.

I propose either finding another third-party JSON library that has this function or vendoring our own version for use in the SDK with the additional functionality. The JSON library is feature frozen, so this isn't something we can push back upstream.

This would significantly reduce both the time it takes for encode/decode and reduce the code to be much easier to read and follow.

Add Golang snippets to Microsoft Docs

Refactor or remove StorageManager

I'm not 100% sure this should exist, unless we are going to implement several of these. But if we are, then I am suggesting the following changes:

  • StorageManager should become private or its args/return values should become public (it is internal, so it can only be implemented in this repo)
  • Should be composed of multiple interfaces that allow use of slimmer interfaces and reduce naming size
  • Use the namespace to make actions clear
  • Use smaller interfaces when possible instead of StorageManager

Here is a proposal for new interfaces:

type StorageManager interface {
	Tokenser
	Accounter
	MetaDataer
	Serialize() ([]byte, error)
	Deserialize([]byte) error
}

type Tokenser interface {
	Tokens() Tokens
}

type Tokens interface {
	Access() AccessReadWrite
	Refresh() RefreshReadWrite
	ID() IDReadWrite
}

type AccessReadWrite interface{
	Read(homeAccountID string, envAliases []string, realm, clientID string, scopes []string) (accessTokenCacheItem, error)
	Write(accessToken accessTokenCacheItem) error
}

type RefreshReadWrite interface {
	Read(homeAccountID string, envAliases []string, familyID, clientID string) (refreshTokenCacheItem, error)
	Write(refreshToken refreshTokenCacheItem) error
}

type IDReadWrite interface{
	Read(homeAccountID string, envAliases []string, realm, clientID string) (idTokenCacheItem, error)
	Write(idToken *idTokenCacheItem) error
}

type Accounter interface{
	Accounts() Accounts
}

type Accounts interface{
	ReadAll() ([]msalbase.Account, error)
	Read(homeAccountID string, envAliases []string, realm string) (msalbase.Account, error)
	Write(account msalbase.Account) error
	Delete(homeAccountID string, envAliases []string) error
}

type MetaDataer interface{
	MetaData() MetaData
}

type MetaData interface{
	Read(envAliases []string, clientID string) (appMetadata, error)
	Write(appMetadata appMetadata) error
}

Now, this does look like more text, but in essence this becomes a really easy to access interface that is composed (so you can use a smaller surface area like Accounts when only those methods are required). This is the same model that base packages like io use.

Here's and example of use:

// Get an access token:
at, err := sm.Tokens().Access().Read(homeAccountID, envAliases, realm, clientID, scopes []string)
if err != nil {
  // Do something
}

// Read all accounts:
accounts, err := sm.Accounts().ReadAll()
if err != nil {
  // Do something
}

Improve the way errors are returned

For a sample error response from the service like this:

"error":"invalid_client","error_description":"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: 86b167f1-2c86-470c-bdc7-04cddd81d401\r\nCorrelation ID: 1441968e-02a2-4280-adb9-8b8ded58a0a0\r\nTimestamp: 2020-11-09 09:13:06Z","error_codes":[7000218],"timestamp":"2020-11-09 09:13:06Z","trace_id":"86b167f1-2c86-470c-bdc7-04cddd81d401","correlation_id":"1441968e-02a2-4280-adb9-8b8ded58a0a0","error_uri":"https://login.microsoftonline.com/error?code=7000218"

the library just exposes 'invalid_client`
Change this to include the error object as this currently hides the error description and the rest of the fields.

Review all third-party dependencies

We (the Go SDK team) have been burned in the past by poor-quality third-party dependencies. Review the current list to ensure they're required and provide value.

We should also avoid using any third-party type as an "exchange type" (a type exposed in public surface area).

[Cleanup] Remove "Provider" interfaces

A lot of operations take/return an interface with a "Provider" suffix. The naming is not idiomatic, and is a lot of cases the interface can likely be removed and a struct returned instead. A good example of the latter is DeviceCodeResultProvider.

[Cleanup] remove internal package

Internal packages are useful when you want to share, without exporting, code across several packages. With the current implementation, the code is needlessly complex and hard to follow.

Device code flow consider `expired_token` response

Per rfc8628, client polling access token might get a expired_token response:

The "device_code" has expired, and the device authorization
session has concluded. The client MAY commence a new device
authorization request but SHOULD wait for user interaction before
restarting to avoid unnecessary polling.

So we might need to specifically handle this case, e.g. commence a new device authorization request and prompt a new message to user.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.