Add OAuth client internal auth mechanism

This commit is contained in:
Drew DeVault 2020-09-22 12:50:39 -04:00
parent 5206db0eff
commit 646e9d90c4
1 changed files with 70 additions and 3 deletions

View File

@ -145,6 +145,65 @@ func authForUsername(ctx context.Context, username string) (*AuthContext, error)
return &auth, nil
}
func authForOAuthClient(ctx context.Context, clientUUID string) (*AuthContext, error) {
var auth AuthContext
if err := database.WithTx(ctx, &sql.TxOptions{
Isolation: 0,
ReadOnly: true,
}, func(tx *sql.Tx) error {
var (
err error
rows *sql.Rows
)
query := database.
Select(ctx, []string{
`u.id`, `u.username`,
`u.created`, `u.updated`,
`u.email`,
`u.user_type`,
`u.url`, `u.location`, `u.bio`,
`u.suspension_notice`,
}).
From(`"oauth2_client" client`).
Join(`"user" u ON u.id = client.owner_id`).
Where(`client.client_uuid = ?`, clientUUID)
if rows, err = query.RunWith(tx).Query(); err != nil {
panic(err)
}
defer rows.Close()
if !rows.Next() {
if err := rows.Err(); err != nil {
panic(err)
}
return fmt.Errorf("Authenticating for unknown client ID %s", clientUUID)
}
if err := rows.Scan(&auth.UserID, &auth.Username, &auth.Created,
&auth.Updated, &auth.Email, &auth.UserType, &auth.URL, &auth.Location,
&auth.Bio, &auth.SuspensionNotice); err != nil {
panic(err)
}
if rows.Next() {
// TODO: Fetch user info from meta if necessary
if err := rows.Err(); err != nil {
panic(err)
}
panic(errors.New("Multiple matching user accounts; invariant broken"))
}
return nil
}); err != nil {
return nil, err
}
if auth.UserType == USER_SUSPENDED {
return nil, fmt.Errorf(
"Account suspended with the following notice: %s\nContact support",
auth.SuspensionNotice)
}
return &auth, nil
}
type AuthCookie struct {
// The username of the authenticated user
Name string `json:"name"`
@ -185,6 +244,9 @@ type InternalAuth struct {
// An arbitrary identifier for this internal node, e.g. "us-east-3.git.sr.ht"
NodeID string `json:"node_id"`
// Only used by specific meta.sr.ht routes
OAuthClientUUID string `json:"oauth_client_id",omit-empty`
}
func internalAuth(internalNet []*net.IPNet, payload []byte,
@ -224,7 +286,12 @@ func internalAuth(internalNet []*net.IPNet, payload []byte,
authError(w, "Invalid Authorization header", http.StatusForbidden)
}
auth, err := authForUsername(r.Context(), internalAuth.Name)
var auth *AuthContext
if internalAuth.OAuthClientUUID != "" {
auth, err = authForOAuthClient(r.Context(), internalAuth.OAuthClientUUID)
} else {
auth, err = authForUsername(r.Context(), internalAuth.Name)
}
if err != nil {
authError(w, err.Error(), http.StatusForbidden)
return
@ -472,7 +539,7 @@ func OAuth2(token string, hash [64]byte, w http.ResponseWriter,
if ot.Grants != "" {
auth.Access = make(map[string]string)
for _, grant := range strings.Split(ot.Grants, ",") {
for _, grant := range strings.Split(ot.Grants, " ") {
var (
service string
scope string
@ -480,7 +547,7 @@ func OAuth2(token string, hash [64]byte, w http.ResponseWriter,
)
parts := strings.Split(grant, "/")
if len(parts) != 2 {
panic(errors.New("OAuth grant without service/scope format"))
panic(fmt.Errorf("OAuth grant '%s' without service/scope format", grant))
}
service = parts[0]
parts = strings.Split(parts[1], ":")