Flashii ID auth provider.
This commit is contained in:
parent
73d9e14e80
commit
1e8940c65d
4 changed files with 276 additions and 0 deletions
1
public/assets/img/svg/gitea-flashii.svg
generated
Normal file
1
public/assets/img/svg/gitea-flashii.svg
generated
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g id="g285"><path id="rect18" d="M13.564,9.491l-1.16,4.327l-0.778,-0.208l1.16,-4.328l0.778,0.209Z" style="fill:#846d77;"/><path id="rect16" d="M12.776,7.254l-1.159,4.328l-0.778,-0.209l1.159,-4.327l0.778,0.208Z" style="fill:#826879;"/><path id="rect14" d="M11.679,6.174l-1.159,4.327l-0.778,-0.208l1.159,-4.327l0.778,0.208Z" style="fill:#7e647a;"/><path id="rect12" d="M10.376,5.865l-1.16,4.327l-0.778,-0.209l1.16,-4.327l0.778,0.209Z" style="fill:#795d7e;"/><path id="rect10" d="M8.969,5.941l-1.16,4.327l-0.778,-0.209l1.16,-4.327l0.778,0.209Z" style="fill:#75567f;"/><path id="rect8" d="M7.562,6.017l-1.16,4.327l-0.778,-0.209l1.16,-4.327l0.778,0.209Z" style="fill:#704f81;"/><path id="rect6" d="M6.258,5.707l-1.159,4.327l-0.778,-0.208l1.159,-4.327l0.778,0.208Z" style="fill:#6b4882;"/><path id="rect4" d="M5.161,4.627l-1.159,4.327l-0.778,-0.208l1.159,-4.328l0.778,0.209Z" style="fill:#674183;"/><path id="rect2" d="M4.374,2.39l-1.16,4.328l-0.778,-0.209l1.16,-4.327l0.778,0.208Z" style="fill:#643b84;"/></g></svg>
|
After Width: | Height: | Size: 1.3 KiB |
186
services/auth/source/oauth2/flashii/flashii.go
Normal file
186
services/auth/source/oauth2/flashii/flashii.go
Normal file
|
@ -0,0 +1,186 @@
|
|||
// Basically copy-pasted from https://github.com/markbates/goth/blob/e55b0146e36b7e1fbaa7453d353861136bf4ed28/providers/github/github.go
|
||||
|
||||
package flashii
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
AuthURL = "https://id.flashii.net/oauth2/authorise"
|
||||
TokenURL = "https://api.flashii.net/oauth2/token"
|
||||
ProfileURL = "https://api.flashii.net/v1/me"
|
||||
)
|
||||
|
||||
var generateCodeVerifier = oauth2.GenerateVerifier
|
||||
|
||||
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
|
||||
return NewCustomisedURL(clientKey, secret, callbackURL, AuthURL, TokenURL, ProfileURL, scopes...)
|
||||
}
|
||||
|
||||
func NewCustomisedURL(clientKey, secret, callbackURL, authURL, tokenURL, profileURL string, scopes ...string) *Provider {
|
||||
p := &Provider{
|
||||
ClientKey: clientKey,
|
||||
Secret: secret,
|
||||
CallbackURL: callbackURL,
|
||||
providerName: "flashii",
|
||||
profileURL: profileURL,
|
||||
GenerateCodeVerifier: generateCodeVerifier,
|
||||
}
|
||||
p.config = newConfig(p, authURL, tokenURL, scopes)
|
||||
return p
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
ClientKey string
|
||||
Secret string
|
||||
CallbackURL string
|
||||
HTTPClient *http.Client
|
||||
config *oauth2.Config
|
||||
providerName string
|
||||
profileURL string
|
||||
GenerateCodeVerifier func() string
|
||||
}
|
||||
|
||||
func (p *Provider) Name() string {
|
||||
return p.providerName
|
||||
}
|
||||
|
||||
func (p *Provider) SetName(name string) {
|
||||
p.providerName = name
|
||||
}
|
||||
|
||||
func (p *Provider) Client() *http.Client {
|
||||
return goth.HTTPClientWithFallBack(p.HTTPClient)
|
||||
}
|
||||
|
||||
func (p *Provider) Debug(debug bool) {}
|
||||
|
||||
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
|
||||
if p.GenerateCodeVerifier == nil {
|
||||
p.GenerateCodeVerifier = generateCodeVerifier
|
||||
}
|
||||
|
||||
verifier := p.GenerateCodeVerifier()
|
||||
url := p.config.AuthCodeURL(state, oauth2.S256ChallengeOption(verifier))
|
||||
session := &Session{
|
||||
AuthURL: url,
|
||||
CodeVerifier: verifier,
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
|
||||
sess := session.(*Session)
|
||||
user := goth.User{
|
||||
AccessToken: sess.AccessToken,
|
||||
Provider: p.Name(),
|
||||
RefreshToken: sess.RefreshToken,
|
||||
}
|
||||
|
||||
if user.AccessToken == "" {
|
||||
return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", p.profileURL, nil)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", "Bearer "+sess.AccessToken)
|
||||
response, err := p.Client().Do(req)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return user, fmt.Errorf("Flashii API responded with a %d trying to fetch user information", response.StatusCode)
|
||||
}
|
||||
|
||||
bits, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
err = userFromReader(bytes.NewReader(bits), &user)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return user, err
|
||||
}
|
||||
|
||||
func userFromReader(reader io.Reader, user *goth.User) error {
|
||||
type FlashiiUserAvatarRes struct {
|
||||
Resolution int32 `json:"res"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
u := struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Title string `json:"title"`
|
||||
AvatarURLs []FlashiiUserAvatarRes `json:"avatar_urls"`
|
||||
}{}
|
||||
|
||||
err := json.NewDecoder(reader).Decode(&u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.UserID = u.Id
|
||||
user.NickName = u.Name
|
||||
user.Email = u.Email
|
||||
user.Description = u.Title
|
||||
user.AvatarURL = u.AvatarURLs[0].URL
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func newConfig(provider *Provider, authURL, tokenURL string, scopes []string) *oauth2.Config {
|
||||
c := &oauth2.Config{
|
||||
ClientID: provider.ClientKey,
|
||||
ClientSecret: provider.Secret,
|
||||
RedirectURL: provider.CallbackURL,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: authURL,
|
||||
TokenURL: tokenURL,
|
||||
},
|
||||
Scopes: []string{},
|
||||
}
|
||||
|
||||
for _, scope := range scopes {
|
||||
c.Scopes = append(c.Scopes, scope)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (p *Provider) RefreshTokenAvailable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
|
||||
token := &oauth2.Token{RefreshToken: refreshToken}
|
||||
ts := p.config.TokenSource(goth.ContextForClient(p.Client()), token)
|
||||
newToken, err := ts.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newToken, err
|
||||
}
|
74
services/auth/source/oauth2/flashii/session.go
Normal file
74
services/auth/source/oauth2/flashii/session.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Basically copy-pasted from https://github.com/markbates/goth/blob/e55b0146e36b7e1fbaa7453d353861136bf4ed28/providers/gitlab/session.go
|
||||
|
||||
package flashii
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
AuthURL string
|
||||
AccessToken string
|
||||
RefreshToken string
|
||||
ExpiresAt time.Time
|
||||
CodeVerifier string
|
||||
}
|
||||
|
||||
var _ goth.Session = &Session{}
|
||||
|
||||
func (s Session) GetAuthURL() (string, error) {
|
||||
if s.AuthURL == "" {
|
||||
return "", errors.New(goth.NoAuthUrlErrorMessage)
|
||||
}
|
||||
return s.AuthURL, nil
|
||||
}
|
||||
|
||||
func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
|
||||
p := provider.(*Provider)
|
||||
|
||||
var authParams []oauth2.AuthCodeOption
|
||||
|
||||
redirectURL := params.Get("redirect_uri")
|
||||
if redirectURL != "" {
|
||||
authParams = append(authParams, oauth2.SetAuthURLParam("redirect_uri", redirectURL))
|
||||
}
|
||||
|
||||
if s.CodeVerifier != "" {
|
||||
authParams = append(authParams, oauth2.VerifierOption(s.CodeVerifier))
|
||||
}
|
||||
|
||||
token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"), authParams...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !token.Valid() {
|
||||
return "", errors.New("Invalid token received from provider")
|
||||
}
|
||||
|
||||
s.AccessToken = token.AccessToken
|
||||
s.RefreshToken = token.RefreshToken
|
||||
s.ExpiresAt = token.Expiry
|
||||
return token.AccessToken, err
|
||||
}
|
||||
|
||||
func (s Session) Marshal() string {
|
||||
b, _ := json.Marshal(s)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (s Session) String() string {
|
||||
return s.Marshal()
|
||||
}
|
||||
|
||||
func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
|
||||
s := &Session{}
|
||||
err := json.NewDecoder(strings.NewReader(data)).Decode(s)
|
||||
return s, err
|
||||
}
|
|
@ -5,6 +5,7 @@ package oauth2
|
|||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2/flashii"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/azureadv2"
|
||||
|
@ -52,6 +53,20 @@ func NewCustomProvider(name, displayName string, customURLSetting *CustomURLSett
|
|||
var _ GothProvider = &CustomProvider{}
|
||||
|
||||
func init() {
|
||||
RegisterGothProvider(NewCustomProvider(
|
||||
"flashii", "Flashii ID", &CustomURLSettings{
|
||||
TokenURL: availableAttribute(flashii.TokenURL),
|
||||
AuthURL: availableAttribute(flashii.AuthURL),
|
||||
ProfileURL: availableAttribute(flashii.ProfileURL),
|
||||
},
|
||||
func(clientID, secret, callbackURL string, custom *CustomURLMapping, scopes []string) (goth.Provider, error) {
|
||||
scopes = append(scopes, "identify")
|
||||
if setting.OAuth2Client.EnableAutoRegistration {
|
||||
scopes = append(scopes, "identify:email")
|
||||
}
|
||||
return flashii.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL, scopes...), nil
|
||||
}))
|
||||
|
||||
RegisterGothProvider(NewCustomProvider(
|
||||
"github", "GitHub", &CustomURLSettings{
|
||||
TokenURL: availableAttribute(github.TokenURL),
|
||||
|
|
Loading…
Reference in a new issue