mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-12-19 22:52:30 +00:00
feat: get claims from github and google
This commit is contained in:
4
frontend/src/index.css
Normal file
4
frontend/src/index.css
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
span,
|
||||||
|
p {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import { TotpPage } from "./pages/totp-page.tsx";
|
|||||||
import { AppContextProvider } from "./context/app-context.tsx";
|
import { AppContextProvider } from "./context/app-context.tsx";
|
||||||
import "./lib/i18n/i18n.ts";
|
import "./lib/i18n/i18n.ts";
|
||||||
import { ForgotPasswordPage } from "./pages/forgot-password-page.tsx";
|
import { ForgotPasswordPage } from "./pages/forgot-password-page.tsx";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
@@ -38,7 +39,10 @@ createRoot(document.getElementById("root")!).render(
|
|||||||
<Route path="/continue" element={<ContinuePage />} />
|
<Route path="/continue" element={<ContinuePage />} />
|
||||||
<Route path="/unauthorized" element={<UnauthorizedPage />} />
|
<Route path="/unauthorized" element={<UnauthorizedPage />} />
|
||||||
<Route path="/error" element={<InternalServerError />} />
|
<Route path="/error" element={<InternalServerError />} />
|
||||||
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
<Route
|
||||||
|
path="/forgot-password"
|
||||||
|
element={<ForgotPasswordPage />}
|
||||||
|
/>
|
||||||
<Route path="*" element={<NotFoundPage />} />
|
<Route path="*" element={<NotFoundPage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|||||||
@@ -283,6 +283,12 @@ func (auth *Auth) OAuthGroup(c *gin.Context, context types.UserContext, labels t
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we are using the generic oauth provider
|
||||||
|
if context.Provider != "generic" {
|
||||||
|
log.Debug().Msg("Not using generic provider, skipping group check")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Split the groups by comma (no need to parse since they are from the API response)
|
// Split the groups by comma (no need to parse since they are from the API response)
|
||||||
oauthGroups := strings.Split(context.OAuthGroups, ",")
|
oauthGroups := strings.Split(context.OAuthGroups, ",")
|
||||||
|
|
||||||
|
|||||||
@@ -150,11 +150,20 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build query
|
// Values
|
||||||
queries, err := query.Values(types.UnauthorizedQuery{
|
values := types.UnauthorizedQuery{
|
||||||
Username: userContext.Username,
|
|
||||||
Resource: strings.Split(host, ".")[0],
|
Resource: strings.Split(host, ".")[0],
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Use either username or email
|
||||||
|
if userContext.OAuth {
|
||||||
|
values.Username = userContext.Email
|
||||||
|
} else {
|
||||||
|
values.Username = userContext.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build query
|
||||||
|
queries, err := query.Values(values)
|
||||||
|
|
||||||
// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik)
|
// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -190,12 +199,21 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build query
|
// Values
|
||||||
queries, err := query.Values(types.UnauthorizedQuery{
|
values := types.UnauthorizedQuery{
|
||||||
Username: userContext.Username,
|
|
||||||
Resource: strings.Split(host, ".")[0],
|
Resource: strings.Split(host, ".")[0],
|
||||||
GroupErr: true,
|
GroupErr: true,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Use either username or email
|
||||||
|
if userContext.OAuth {
|
||||||
|
values.Username = userContext.Email
|
||||||
|
} else {
|
||||||
|
values.Username = userContext.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build query
|
||||||
|
queries, err := query.Values(values)
|
||||||
|
|
||||||
// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik)
|
// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -10,30 +10,36 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Github has a different response than the generic provider
|
// Response for the github email endpoint
|
||||||
type GithubUserInfoResponse []struct {
|
type GithubEmailResponse []struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Primary bool `json:"primary"`
|
Primary bool `json:"primary"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response for the github user endpoint
|
||||||
|
type GithubUserInfoResponse struct {
|
||||||
|
Login string `json:"login"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
// The scopes required for the github provider
|
// The scopes required for the github provider
|
||||||
func GithubScopes() []string {
|
func GithubScopes() []string {
|
||||||
return []string{"user:email"}
|
return []string{"user:email", "read:user"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGithubUser(client *http.Client) (constants.Claims, error) {
|
func GetGithubUser(client *http.Client) (constants.Claims, error) {
|
||||||
// Create user struct
|
// Create user struct
|
||||||
var user constants.Claims
|
var user constants.Claims
|
||||||
|
|
||||||
// Get the user emails from github using the oauth http client
|
// Get the user info from github using the oauth http client
|
||||||
res, err := client.Get("https://api.github.com/user/emails")
|
res, err := client.Get("https://api.github.com/user")
|
||||||
|
|
||||||
// Check if there was an error
|
// Check if there was an error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msg("Got response from github")
|
log.Debug().Msg("Got user response from github")
|
||||||
|
|
||||||
// Read the body of the response
|
// Read the body of the response
|
||||||
body, err := io.ReadAll(res.Body)
|
body, err := io.ReadAll(res.Body)
|
||||||
@@ -43,10 +49,41 @@ func GetGithubUser(client *http.Client) (constants.Claims, error) {
|
|||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msg("Read body from github")
|
log.Debug().Msg("Read user body from github")
|
||||||
|
|
||||||
// Parse the body into a user struct
|
// Parse the body into a user struct
|
||||||
var emails GithubUserInfoResponse
|
var userInfo GithubUserInfoResponse
|
||||||
|
|
||||||
|
// Unmarshal the body into the user struct
|
||||||
|
err = json.Unmarshal(body, &userInfo)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the user emails from github using the oauth http client
|
||||||
|
res, err = client.Get("https://api.github.com/user/emails")
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msg("Got email response from github")
|
||||||
|
|
||||||
|
// Read the body of the response
|
||||||
|
body, err = io.ReadAll(res.Body)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msg("Read email body from github")
|
||||||
|
|
||||||
|
// Parse the body into a user struct
|
||||||
|
var emails GithubEmailResponse
|
||||||
|
|
||||||
// Unmarshal the body into the user struct
|
// Unmarshal the body into the user struct
|
||||||
err = json.Unmarshal(body, &emails)
|
err = json.Unmarshal(body, &emails)
|
||||||
@@ -61,11 +98,26 @@ func GetGithubUser(client *http.Client) (constants.Claims, error) {
|
|||||||
// Find and return the primary email
|
// Find and return the primary email
|
||||||
for _, email := range emails {
|
for _, email := range emails {
|
||||||
if email.Primary {
|
if email.Primary {
|
||||||
|
// Set the email then exit
|
||||||
user.Email = email.Email
|
user.Email = email.Email
|
||||||
return user, nil
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// User does not have a primary email?
|
// If no primary email was found, use the first available email
|
||||||
return user, errors.New("no primary email found")
|
if len(emails) == 0 {
|
||||||
|
return user, errors.New("no emails found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the email if it is not set picking the first one
|
||||||
|
if user.Email == "" {
|
||||||
|
user.Email = emails[0].Email
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the username and name
|
||||||
|
user.PreferredUsername = userInfo.Login
|
||||||
|
user.Name = userInfo.Name
|
||||||
|
|
||||||
|
// Return
|
||||||
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,21 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"tinyauth/internal/constants"
|
"tinyauth/internal/constants"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Response for the google user endpoint
|
||||||
|
type GoogleUserInfoResponse struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
// The scopes required for the google provider
|
// The scopes required for the google provider
|
||||||
func GoogleScopes() []string {
|
func GoogleScopes() []string {
|
||||||
return []string{"https://www.googleapis.com/auth/userinfo.email"}
|
return []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGoogleUser(client *http.Client) (constants.Claims, error) {
|
func GetGoogleUser(client *http.Client) (constants.Claims, error) {
|
||||||
@@ -38,8 +45,11 @@ func GetGoogleUser(client *http.Client) (constants.Claims, error) {
|
|||||||
|
|
||||||
log.Debug().Msg("Read body from google")
|
log.Debug().Msg("Read body from google")
|
||||||
|
|
||||||
|
// Create a new user info struct
|
||||||
|
var userInfo GoogleUserInfoResponse
|
||||||
|
|
||||||
// Unmarshal the body into the user struct
|
// Unmarshal the body into the user struct
|
||||||
err = json.Unmarshal(body, &user)
|
err = json.Unmarshal(body, &userInfo)
|
||||||
|
|
||||||
// Check if there was an error
|
// Check if there was an error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -48,6 +58,11 @@ func GetGoogleUser(client *http.Client) (constants.Claims, error) {
|
|||||||
|
|
||||||
log.Debug().Msg("Parsed user from google")
|
log.Debug().Msg("Parsed user from google")
|
||||||
|
|
||||||
|
// Map the user info to the user struct
|
||||||
|
user.PreferredUsername = strings.Split(userInfo.Email, "@")[0]
|
||||||
|
user.Name = userInfo.Name
|
||||||
|
user.Email = userInfo.Email
|
||||||
|
|
||||||
// Return the user
|
// Return the user
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user