-
Notifications
You must be signed in to change notification settings - Fork 0
/
auth.go
127 lines (109 loc) · 3.96 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package lil
import (
"context"
"fmt"
"time"
"golang.org/x/oauth2"
google "google.golang.org/api/oauth2/v2"
"google.golang.org/api/option"
)
// Authentication providers.
const (
AuthSourceGitHub = "github"
AuthSourceGoogle = "google"
)
// Auth represents a set of OAuth credentials. These are linked to a User so a
// single user could authenticate through multiple providers.
//
// The authentication system links users by email address, however, some GitHub
// users don't provide their email publicly so we may not be able to link them
// by email address. It's a moot point, however, as we only support GitHub as
// an OAuth provider.
type Auth struct {
ID int `json:"id"`
// User can have one or more methods of authentication.
// However, only one per source is allowed per user.
UserID int `json:"userID"`
User *User `json:"user"`
// The authentication source & the source provider's user ID.
// Source can only be "github" currently.
Source string `json:"source"`
SourceID string `json:"sourceID"`
// OAuth fields returned from the authentication provider.
// GitHub does not use refresh tokens but the field exists for future providers.
AccessToken string `json:"-"`
RefreshToken string `json:"-"`
Expiry *time.Time `json:"-"`
// Timestamps of creation & last update.
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// Validate returns an error if any fields are invalid on the Auth object.
// This can be called by the SQLite implementation to do some basic checks.
func (a *Auth) Validate() error {
if a.UserID == 0 {
return Errorf(EINVALID, "User required.")
} else if a.Source == "" {
return Errorf(EINVALID, "Source required.")
} else if a.SourceID == "" {
return Errorf(EINVALID, "Source ID required.")
} else if a.AccessToken == "" {
return Errorf(EINVALID, "Access token required.")
}
return nil
}
// AvatarURL returns a URL to the avatar image hosted by the authentication source.
// Returns an empty string if the authentication source is invalid.
func (a *Auth) AvatarURL(size int) string {
switch a.Source {
case AuthSourceGitHub:
return fmt.Sprintf("https://avatars1.githubusercontent.com/u/%s?s=%d", a.SourceID, size)
case AuthSourceGoogle:
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
g, err := google.NewService(ctx, option.WithTokenSource(oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: a.AccessToken},
)))
if err != nil {
return ""
}
c, err := google.NewUserinfoV2Service(g).Me.Get().Do()
if err != nil {
return ""
}
return c.Picture
default:
return ""
}
}
// AuthService represents a service for managing auths.
type AuthService interface {
// Looks up an authentication object by ID along with the associated user.
// Returns ENOTFOUND if ID does not exist.
FindAuthByID(ctx context.Context, id int) (*Auth, error)
// Retrieves authentication objects based on a filter. Also returns the
// total number of objects that match the filter. This may differ from the
// returned object count if the Limit field is set.
FindAuths(ctx context.Context, filter AuthFilter) ([]*Auth, int, error)
// Creates a new authentication object If a User is attached to auth, then
// the auth object is linked to an existing user. Otherwise a new user
// object is created.
//
// On success, the auth.ID is set to the new authentication ID.
CreateAuth(ctx context.Context, auth *Auth) error
// Permanently deletes an authentication object from the system by ID.
// The parent user object is not removed.
DeleteAuth(ctx context.Context, id int) error
}
// AuthFilter represents a filter accepted by FindAuths().
type AuthFilter struct {
// Filtering fields.
ID *int `json:"id"`
UserID *int `json:"userID"`
Source *string `json:"source"`
SourceID *string `json:"sourceID"`
// Restricts results to a subset of the total range.
// Can be used for pagination.
Offset int `json:"offset"`
Limit int `json:"limit"`
}