- dev auth flow (side-step OAuth) - db event processing integration tests - dev scripts (eg Makefile) - db / test db migration setup scripts.
98 lines
2.3 KiB
Go
98 lines
2.3 KiB
Go
package authentication
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
// DevLogin mints a local session for userID without going through the real
|
|
// Auth0 OAuth flow. It writes a real oauth_users/oauth_tokens row, so every
|
|
// other code path (identity lookup, account linking/creation, cookie
|
|
// handling) treats it exactly like a normal login.
|
|
//
|
|
// Only ever call this from a route gated on an explicit dev-mode flag -
|
|
// never wire it up unconditionally, since it lets the caller authenticate
|
|
// as any user_id with no credentials.
|
|
func (a *Authenticator) DevLogin(ctx context.Context, userID, name string) (accessToken string, expiration time.Time, err error) {
|
|
tokenBytes := make([]byte, 32)
|
|
if _, err := rand.Read(tokenBytes); err != nil {
|
|
return "", time.Time{}, fmt.Errorf("failed to generate access token: %w", err)
|
|
}
|
|
accessToken = "dev_" + hex.EncodeToString(tokenBytes)
|
|
|
|
// Set far in the future so the near-expiry refresh path in
|
|
// server/auth never tries to refresh a token that has no real
|
|
// Auth0-side refresh token behind it.
|
|
expiration = time.Now().Add(365 * 24 * time.Hour)
|
|
|
|
if _, err = a.db.Exec(
|
|
ctx,
|
|
`
|
|
WITH ensured_user AS (
|
|
INSERT INTO oauth_users (user_id)
|
|
VALUES (@user_id)
|
|
ON CONFLICT DO NOTHING
|
|
)
|
|
INSERT INTO oauth_tokens (
|
|
access_token,
|
|
token_type,
|
|
refresh_token,
|
|
expiry,
|
|
|
|
id_token_issuer,
|
|
id_token_audience,
|
|
id_token_subject,
|
|
id_token_expiry,
|
|
id_token_issued_at,
|
|
id_token_nonce,
|
|
id_token_access_token_hash,
|
|
|
|
id_token_custom_claims_family_name,
|
|
id_token_custom_claims_given_name,
|
|
id_token_custom_claims_name,
|
|
id_token_custom_claims_nickname,
|
|
id_token_custom_claims_picture,
|
|
id_token_custom_claims_updated_at
|
|
)
|
|
VALUES (
|
|
@access_token,
|
|
'dev',
|
|
'',
|
|
@expiry,
|
|
|
|
'dev-auth-bypass',
|
|
@audience,
|
|
@user_id,
|
|
@expiry,
|
|
NOW(),
|
|
'',
|
|
'',
|
|
|
|
@name,
|
|
@name,
|
|
@name,
|
|
@name,
|
|
'',
|
|
NOW()
|
|
)
|
|
`,
|
|
pgx.NamedArgs{
|
|
"access_token": accessToken,
|
|
"expiry": expiration,
|
|
"user_id": userID,
|
|
"name": name,
|
|
"audience": pgtype.FlatArray[string]{"dev"},
|
|
},
|
|
); err != nil {
|
|
return "", time.Time{}, fmt.Errorf("failed to save dev session: %w", err)
|
|
}
|
|
|
|
return accessToken, expiration, nil
|
|
}
|