azureadv2 won't request a client_id token
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.
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)
}
Were you able to get this done?
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.
were you able to conclude the implementation to support multiple tenant or rather accept tenant while requesting auth...!! TIA
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).