goth icon indicating copy to clipboard operation
goth copied to clipboard

azureadv2 won't request a client_id token

Open andradei opened this issue 5 years ago • 5 comments

I used example.go as a base, the program authenticates, but after that it doesn't save a session, I think it doesn't make a request to the token endpoint.

What happens is I only get to see the user info (see the HTML template below) if I render the page from withing the /signin handler. If I get out of that page or redirect, the data is lost and I get an error that says the session couldn't be found.

andradei avatar Aug 20 '20 16:08 andradei

Here is the code:

package main

import (
        "fmt"
        "html/template"
        "log"
        "net/http"
        "os"

        "github.com/gorilla/sessions"
        "github.com/markbates/goth"
        "github.com/markbates/goth/gothic"
        "github.com/markbates/goth/providers/azureadv2"
)

const (
        redirectURI  = "https://localhost:8080/signin?provider=azureadv2"
        clientID     = "a-client-id"
        clientSecret = "a-client-secret"
        // NOTE: Required to connect to non-microsoft-officially-supported app.
        //       Otherwise, azureadv2.TenantType would suffice.
        tenantID              = "a-tenant-id"
        authorizationEndpoint = "https://login.microsoftonline.com/some-hash/oauth2/authorize"
        tokenEndpoint         = "https://login.microsoftonline.com/some-hash/oauth2/token"
)

var (
        page = template.Must(template.New("").Parse(`<!doctype html>
<html lang="en">

<head>
        <meta charset="utf-8">
        <title>Auth Practice</title>
        <link rel="stylesheet" href="styles.css">
</head>

<body>
        <h1>Azure AD Authentication Practice</h1>
        {{print .}}
{{with .User}}
        <a href="/logout?provider=azureadv2">logout</a>
        <div>
                <p>Name: {{.Name}} [{{.LastName}}, {{.FirstName}}]</p>
                <p>Email: {{.Email}}</p>
                <p>NickName: {{.NickName}}</p>
                <p>Location: {{.Location}}</p>
                <p>AvatarURL: {{.AvatarURL}} <img src="{{.AvatarURL}}"></p>
                <p>Description: {{.Description}}</p>
                <p>UserID: {{.UserID}}</p>
                <p>AccessToken: {{.AccessToken}}</p>
                <p>AccessTokenSecret: {{.AccessTokenSecret}}</p>
                <p>ExpiresAt: {{.ExpiresAt}}</p>
                <p>RefreshToken: {{.RefreshToken}}</p>
                <p>IDToken: {{.IDToken}}</p>
        </div>
{{else}}
        <a href="/login?provider=azureadv2">login</a>
{{end}}
        <script src="js/scripts.js"></script>
</body>

</html>`))
)

type pageData struct {
        User goth.User
}

func main() {
        // Create a list of providers with only AzureAD v2.
        options := azureadv2.ProviderOptions{
                Scopes: []azureadv2.ScopeType{azureadv2.UserReadScope},
                Tenant: tenantID,
        }
        goth.UseProviders(
                azureadv2.New(clientID, clientSecret, redirectURI, options),
        )

        // Set up a custom session store to satisfy Azure AD requirements.
        // See: https://github.com/markbates/goth#security-notes
        store := sessions.NewCookieStore([]byte(os.Getenv("SESSION_SECRET")))
        store.MaxAge(86400 * 30) // 30 days
        store.Options.Path = "/"
        store.Options.HttpOnly = true // should always be true
        store.Options.Secure = true   // Azure AD enforces HTTPS on redirect URLs

        gothic.Store = store

        // Routes
        http.HandleFunc("/", home)
        http.HandleFunc("/login", login)
        http.HandleFunc("/signin", signin)

        fmt.Println("serving on https://localhost:8080")
        log.Panic(http.ListenAndServeTLS(":8080", "certs/cert.pem", "certs/key.pem", nil))
}

func home(w http.ResponseWriter, r *http.Request) {
        if err := page.Execute(w, nil); err != nil {
                log.Panic("error serving home page:", err)
        }
}

func login(w http.ResponseWriter, r *http.Request) {
        // If user is already authenticated, retrieve it.
        if user, err := gothic.CompleteUserAuth(w, r); err == nil {
                pg := pageData{user}
                if err := page.Execute(w, pg); err != nil {
                        log.Panic("error serving logged-in home page:", err)

                }
        } else {
                gothic.BeginAuthHandler(w, r)
        }
}

// Azure AD will redirect to this page after a login.
func signin(w http.ResponseWriter, r *http.Request) {
        user, err := gothic.CompleteUserAuth(w, r)
        if err != nil {
                log.Panic("error after auth redirect:", err)
        }

        log.Println("user logged on:", user)

        if err := page.Execute(w, user); err != nil {
                log.Panic("error rendering page with user:", err)
        }

        gothic.StoreInSession()
        session, err := gothic.Store.Get(r, gothic.SessionName)
        if err != nil {
                log.Panic("error getting session store:", err)
        }

}

func logout(w http.ResponseWriter, r *http.Request) {
        if err := gothic.Logout(w, r); err != nil {
                log.Panic("error logging out:", err)
        }

        // Redirect to the home page
        w.Header().Set("Location", "/")
        w.WriteHeader(http.StatusTemporaryRedirect)
}

andradei avatar Aug 20 '20 16:08 andradei

Were you able to get this done?

stupidly-logical avatar Oct 06 '20 10:10 stupidly-logical

No. I had to write the code from scratch myself. It is mostly complete, I only need to figure out how to verify the JWT once the authentication process is finished.

andradei avatar Nov 13 '20 03:11 andradei

were you able to conclude the implementation to support multiple tenant or rather accept tenant while requesting auth...!! TIA

cc-shekher avatar Nov 03 '22 14:11 cc-shekher

were you able to conclude the implementation to support multiple tenant or rather accept tenant while requesting auth...!! TIA

No. The code I ended up implementing is closed source and I don't have access to it anymore. I also didn't try to implement multi-tenancy. Sorry, I wish I could help more. But it looks like there are better auth packages available since 2020 (like https://github.com/volatiletech/authboss).

andradei avatar Nov 08 '22 04:11 andradei