Compare commits
16 Commits
0.1.0-beta
...
0.3.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cb5b02d777 | ||
![]() |
6ca37a0d50 | ||
![]() |
a9cf301344 | ||
![]() |
0305a719db | ||
![]() |
4269e2242c | ||
![]() |
71e4e35de6 | ||
![]() |
3aeb3b8d67 | ||
![]() |
e1951bfbe0 | ||
![]() |
1299ec5f9c | ||
![]() |
bc53974d2a | ||
![]() |
3ed5426467 | ||
![]() |
29251c8c20 | ||
![]() |
08b1f97ccb | ||
![]() |
abc2991e1c | ||
![]() |
b69d0b8e23 | ||
![]() |
86d781b210 |
@@ -9,3 +9,4 @@ ROLES=user
|
|||||||
DEFAULT_ROLES=user
|
DEFAULT_ROLES=user
|
||||||
PROTECTED_ROLES=admin
|
PROTECTED_ROLES=admin
|
||||||
JWT_ROLE_CLAIM=role
|
JWT_ROLE_CLAIM=role
|
||||||
|
CUSTOM_ACCESS_TOKEN_SCRIPT=function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}
|
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -10,7 +10,7 @@ We're so excited you're interested in helping with Authorizer! We are happy to h
|
|||||||
## Where to ask questions?
|
## Where to ask questions?
|
||||||
|
|
||||||
1. Check our [Github Issues](https://github.com/authorizerdev/authorizer/issues) to see if someone has already answered your question.
|
1. Check our [Github Issues](https://github.com/authorizerdev/authorizer/issues) to see if someone has already answered your question.
|
||||||
2. Join our community on [Discord](https://discord.gg/WDvCxwkX) and feel free to ask us your questions
|
2. Join our community on [Discord](https://discord.gg/Zv2D5h6kkK) and feel free to ask us your questions
|
||||||
|
|
||||||
As you gain experience with Authorizer, please help answer other people's questions! :pray:
|
As you gain experience with Authorizer, please help answer other people's questions! :pray:
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ As you gain experience with Authorizer, please help answer other people's questi
|
|||||||
You can get started by taking a look at our [Github issues](https://github.com/authorizerdev/authorizer/issues)
|
You can get started by taking a look at our [Github issues](https://github.com/authorizerdev/authorizer/issues)
|
||||||
If you find one that looks interesting and no one else is already working on it, comment on that issue and start contributing 🙂.
|
If you find one that looks interesting and no one else is already working on it, comment on that issue and start contributing 🙂.
|
||||||
|
|
||||||
Please ask as many questions as you need, either directly in the issue or on [Discord](https://discord.gg/WDvCxwkX). We're happy to help!:raised_hands:
|
Please ask as many questions as you need, either directly in the issue or on [Discord](https://discord.gg/Zv2D5h6kkK). We're happy to help!:raised_hands:
|
||||||
|
|
||||||
### Contributions that are ALWAYS welcome
|
### Contributions that are ALWAYS welcome
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ ARG VERSION="latest"
|
|||||||
ENV VERSION="$VERSION"
|
ENV VERSION="$VERSION"
|
||||||
|
|
||||||
RUN echo "$VERSION"
|
RUN echo "$VERSION"
|
||||||
RUN apk add build-base &&\
|
RUN apk add build-base nodejs &&\
|
||||||
make clean && make && \
|
make clean && make && \
|
||||||
chmod 777 build/server
|
chmod 777 build/server
|
||||||
|
|
||||||
|
23
README.md
23
README.md
@@ -15,7 +15,7 @@
|
|||||||
- [Getting Started](#getting-started)
|
- [Getting Started](#getting-started)
|
||||||
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
|
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
|
||||||
- [Docs](http://docs.authorizer.dev/)
|
- [Docs](http://docs.authorizer.dev/)
|
||||||
- [Join Community](https://discord.gg/2fXUQN3E)
|
- [Join Community](https://discord.gg/Zv2D5h6kkK)
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
@@ -30,14 +30,10 @@
|
|||||||
- ✅ Forgot password flow using email
|
- ✅ Forgot password flow using email
|
||||||
- ✅ Social logins (Google, Github, Facebook, more coming soon)
|
- ✅ Social logins (Google, Github, Facebook, more coming soon)
|
||||||
- ✅ Role-based access management
|
- ✅ Role-based access management
|
||||||
|
- ✅ Password-less login with email and magic link
|
||||||
## Project Status
|
|
||||||
|
|
||||||
⚠️ **Authorizer is still an early beta! missing features and bugs are to be expected!** If you can stomach it, then bring authentication and authorization to your site today!
|
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- Password-less login with email and magic link
|
|
||||||
- Support more JWT encryption algorithms (Currently supporting HS256)
|
- Support more JWT encryption algorithms (Currently supporting HS256)
|
||||||
- 2 Factor authentication
|
- 2 Factor authentication
|
||||||
- Back office (Admin dashboard to manage user)
|
- Back office (Admin dashboard to manage user)
|
||||||
@@ -95,13 +91,12 @@ binaries are baked with required deployment files and bundled. You can download
|
|||||||
|
|
||||||
- Mac OSX
|
- Mac OSX
|
||||||
- Linux
|
- Linux
|
||||||
- Windows
|
|
||||||
|
|
||||||
### Step 1: Download and unzip bundle
|
### Step 1: Download and unzip bundle
|
||||||
|
|
||||||
- Download the Bundle for the specific OS from the [release page](https://github.com/authorizerdev/authorizer/releases)
|
- Download the Bundle for the specific OS from the [release page](https://github.com/authorizerdev/authorizer/releases)
|
||||||
|
|
||||||
> Note: For windows, it includes `.zip` file. For Linux & MacOS, it includes `.tar.gz` file.
|
> Note: For windows, we recommend running using docker image to run authorizer.
|
||||||
|
|
||||||
- Unzip using following command
|
- Unzip using following command
|
||||||
|
|
||||||
@@ -111,12 +106,6 @@ binaries are baked with required deployment files and bundled. You can download
|
|||||||
tar -zxf AUTHORIZER_VERSION -c authorizer
|
tar -zxf AUTHORIZER_VERSION -c authorizer
|
||||||
```
|
```
|
||||||
|
|
||||||
- Windows
|
|
||||||
|
|
||||||
```sh
|
|
||||||
unzip AUTHORIZER_VERSION
|
|
||||||
```
|
|
||||||
|
|
||||||
- Change directory to `authorizer`
|
- Change directory to `authorizer`
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -137,12 +126,6 @@ Required environment variables are pre-configured in `.env` file. But based on t
|
|||||||
./build/server
|
./build/server
|
||||||
```
|
```
|
||||||
|
|
||||||
- For windows
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./build/server.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
> Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server`
|
> Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server`
|
||||||
|
|
||||||
## Install instance on Heroku
|
## Install instance on Heroku
|
||||||
|
6
TODO.md
6
TODO.md
@@ -1,5 +1,11 @@
|
|||||||
# Task List
|
# Task List
|
||||||
|
|
||||||
|
## Feature Multiple sessions
|
||||||
|
|
||||||
|
- Multiple sessions for users to login use hMset from redis for this
|
||||||
|
user_id access_token1 long_live_token1
|
||||||
|
user_id access_token2 long_live_token2
|
||||||
|
|
||||||
# Feature roles
|
# Feature roles
|
||||||
|
|
||||||
For the first version we will only support setting roles master list via env
|
For the first version we will only support setting roles master list via env
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
985
app/package-lock.json
generated
985
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,12 +10,13 @@
|
|||||||
"author": "Lakhan Samani",
|
"author": "Lakhan Samani",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.19",
|
"@authorizerdev/authorizer-react": "^0.1.0",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-is": "^17.0.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5"
|
||||||
},
|
},
|
||||||
|
@@ -14,6 +14,7 @@ var (
|
|||||||
JWT_SECRET = ""
|
JWT_SECRET = ""
|
||||||
ALLOWED_ORIGINS = []string{}
|
ALLOWED_ORIGINS = []string{}
|
||||||
AUTHORIZER_URL = ""
|
AUTHORIZER_URL = ""
|
||||||
|
APP_URL = ""
|
||||||
PORT = "8080"
|
PORT = "8080"
|
||||||
REDIS_URL = ""
|
REDIS_URL = ""
|
||||||
IS_PROD = false
|
IS_PROD = false
|
||||||
@@ -21,6 +22,7 @@ var (
|
|||||||
RESET_PASSWORD_URL = ""
|
RESET_PASSWORD_URL = ""
|
||||||
DISABLE_EMAIL_VERIFICATION = "false"
|
DISABLE_EMAIL_VERIFICATION = "false"
|
||||||
DISABLE_BASIC_AUTHENTICATION = "false"
|
DISABLE_BASIC_AUTHENTICATION = "false"
|
||||||
|
DISABLE_MAGIC_LOGIN = "false"
|
||||||
|
|
||||||
// ROLES
|
// ROLES
|
||||||
ROLES = []string{}
|
ROLES = []string{}
|
||||||
|
@@ -27,6 +27,7 @@ type Manager interface {
|
|||||||
GetVerificationByEmail(email string) (VerificationRequest, error)
|
GetVerificationByEmail(email string) (VerificationRequest, error)
|
||||||
DeleteUser(email string) error
|
DeleteUser(email string) error
|
||||||
SaveRoles(roles []Role) error
|
SaveRoles(roles []Role) error
|
||||||
|
SaveSession(session Session) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type manager struct {
|
type manager struct {
|
||||||
@@ -56,7 +57,7 @@ func InitDB() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to init db:", err)
|
log.Fatal("Failed to init db:", err)
|
||||||
} else {
|
} else {
|
||||||
db.AutoMigrate(&User{}, &VerificationRequest{}, &Role{})
|
db.AutoMigrate(&User{}, &VerificationRequest{}, &Role{}, &Session{})
|
||||||
}
|
}
|
||||||
|
|
||||||
Mgr = &manager{db: db}
|
Mgr = &manager{db: db}
|
||||||
|
39
server/db/session.go
Normal file
39
server/db/session.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
ID uuid.UUID `gorm:"type:uuid;"`
|
||||||
|
UserID uuid.UUID `gorm:"type:uuid;"`
|
||||||
|
User User
|
||||||
|
UserAgent string
|
||||||
|
IP string
|
||||||
|
CreatedAt int64 `gorm:"autoCreateTime"`
|
||||||
|
UpdatedAt int64 `gorm:"autoUpdateTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Session) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
|
r.ID = uuid.New()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveSession function to save user sessiosn
|
||||||
|
func (mgr *manager) SaveSession(session Session) error {
|
||||||
|
res := mgr.db.Clauses(
|
||||||
|
clause.OnConflict{
|
||||||
|
DoNothing: true,
|
||||||
|
}).Create(&session)
|
||||||
|
if res.Error != nil {
|
||||||
|
log.Println(`Error saving session`, res.Error)
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@@ -30,6 +30,7 @@ func (mgr *manager) AddVerification(verification VerificationRequest) (Verificat
|
|||||||
Columns: []clause.Column{{Name: "email"}},
|
Columns: []clause.Column{{Name: "email"}},
|
||||||
DoUpdates: clause.AssignmentColumns([]string{"token", "identifier", "expires_at"}),
|
DoUpdates: clause.AssignmentColumns([]string{"token", "identifier", "expires_at"}),
|
||||||
}).Create(&verification)
|
}).Create(&verification)
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
log.Println(`Error saving verification record`, result.Error)
|
log.Println(`Error saving verification record`, result.Error)
|
||||||
return verification, result.Error
|
return verification, result.Error
|
||||||
|
@@ -64,6 +64,7 @@ func InitEnv() {
|
|||||||
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
||||||
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION")
|
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION")
|
||||||
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION")
|
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION")
|
||||||
|
constants.DISABLE_MAGIC_LOGIN = os.Getenv("DISABLE_MAGIC_LOGIN")
|
||||||
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
||||||
|
|
||||||
if constants.ADMIN_SECRET == "" {
|
if constants.ADMIN_SECRET == "" {
|
||||||
@@ -126,13 +127,17 @@ func InitEnv() {
|
|||||||
constants.DISABLE_BASIC_AUTHENTICATION = "false"
|
constants.DISABLE_BASIC_AUTHENTICATION = "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
if constants.DISABLE_EMAIL_VERIFICATION == "" && constants.DISABLE_BASIC_AUTHENTICATION == "false" {
|
if constants.DISABLE_MAGIC_LOGIN == "" {
|
||||||
|
constants.DISABLE_MAGIC_LOGIN = "false"
|
||||||
|
}
|
||||||
|
|
||||||
if constants.SMTP_HOST == "" || constants.SENDER_EMAIL == "" || constants.SENDER_PASSWORD == "" {
|
if constants.SMTP_HOST == "" || constants.SENDER_EMAIL == "" || constants.SENDER_PASSWORD == "" {
|
||||||
constants.DISABLE_EMAIL_VERIFICATION = "true"
|
constants.DISABLE_EMAIL_VERIFICATION = "true"
|
||||||
} else {
|
} else if constants.DISABLE_EMAIL_VERIFICATION == "" {
|
||||||
constants.DISABLE_EMAIL_VERIFICATION = "false"
|
constants.DISABLE_EMAIL_VERIFICATION = "false"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
log.Println("=> disable email verification:", constants.DISABLE_EMAIL_VERIFICATION)
|
||||||
|
|
||||||
rolesSplit := strings.Split(os.Getenv("ROLES"), ",")
|
rolesSplit := strings.Split(os.Getenv("ROLES"), ",")
|
||||||
roles := []string{}
|
roles := []string{}
|
||||||
|
@@ -4,6 +4,7 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.13.0
|
github.com/99designs/gqlgen v0.13.0
|
||||||
|
github.com/coreos/go-oidc/v3 v3.1.0 // indirect
|
||||||
github.com/gin-contrib/location v0.0.2 // indirect
|
github.com/gin-contrib/location v0.0.2 // indirect
|
||||||
github.com/gin-gonic/gin v1.7.2
|
github.com/gin-gonic/gin v1.7.2
|
||||||
github.com/go-playground/validator/v10 v10.8.0 // indirect
|
github.com/go-playground/validator/v10 v10.8.0 // indirect
|
||||||
@@ -16,6 +17,7 @@ require (
|
|||||||
github.com/json-iterator/go v1.1.11 // indirect
|
github.com/json-iterator/go v1.1.11 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.7 // indirect
|
github.com/mattn/go-sqlite3 v1.14.7 // indirect
|
||||||
|
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f // indirect
|
||||||
github.com/ugorji/go v1.2.6 // indirect
|
github.com/ugorji/go v1.2.6 // indirect
|
||||||
github.com/vektah/gqlparser/v2 v2.1.0
|
github.com/vektah/gqlparser/v2 v2.1.0
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||||
@@ -28,5 +30,4 @@ require (
|
|||||||
gorm.io/driver/postgres v1.1.0
|
gorm.io/driver/postgres v1.1.0
|
||||||
gorm.io/driver/sqlite v1.1.4
|
gorm.io/driver/sqlite v1.1.4
|
||||||
gorm.io/gorm v1.21.11
|
gorm.io/gorm v1.21.11
|
||||||
rogchap.com/v8go v0.6.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
@@ -109,6 +109,8 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I
|
|||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||||
|
github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
|
||||||
|
github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
@@ -556,6 +558,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
|
|||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/robertkrimen/otto v0.0.0-20211019175142-5b0d97091c6f h1:wOVoULFf7IVSJ9hl8wnQew/kCpchffRb7a81H9/IcS4=
|
github.com/robertkrimen/otto v0.0.0-20211019175142-5b0d97091c6f h1:wOVoULFf7IVSJ9hl8wnQew/kCpchffRb7a81H9/IcS4=
|
||||||
github.com/robertkrimen/otto v0.0.0-20211019175142-5b0d97091c6f/go.mod h1:/mK7FZ3mFYEn9zvNPhpngTyatyehSwte5bJZ4ehL5Xw=
|
github.com/robertkrimen/otto v0.0.0-20211019175142-5b0d97091c6f/go.mod h1:/mK7FZ3mFYEn9zvNPhpngTyatyehSwte5bJZ4ehL5Xw=
|
||||||
|
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f h1:a7clxaGmmqtdNTXyvrp/lVO/Gnkzlhc/+dLs5v965GM=
|
||||||
|
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f/go.mod h1:/mK7FZ3mFYEn9zvNPhpngTyatyehSwte5bJZ4ehL5Xw=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
@@ -735,6 +739,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
@@ -1098,6 +1103,8 @@ gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbR
|
|||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
||||||
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
|
@@ -61,6 +61,7 @@ type ComplexityRoot struct {
|
|||||||
IsFacebookLoginEnabled func(childComplexity int) int
|
IsFacebookLoginEnabled func(childComplexity int) int
|
||||||
IsGithubLoginEnabled func(childComplexity int) int
|
IsGithubLoginEnabled func(childComplexity int) int
|
||||||
IsGoogleLoginEnabled func(childComplexity int) int
|
IsGoogleLoginEnabled func(childComplexity int) int
|
||||||
|
IsMagicLoginEnabled func(childComplexity int) int
|
||||||
IsTwitterLoginEnabled func(childComplexity int) int
|
IsTwitterLoginEnabled func(childComplexity int) int
|
||||||
Version func(childComplexity int) int
|
Version func(childComplexity int) int
|
||||||
}
|
}
|
||||||
@@ -71,6 +72,7 @@ type ComplexityRoot struct {
|
|||||||
ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int
|
ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int
|
||||||
Login func(childComplexity int, params model.LoginInput) int
|
Login func(childComplexity int, params model.LoginInput) int
|
||||||
Logout func(childComplexity int) int
|
Logout func(childComplexity int) int
|
||||||
|
MagicLogin func(childComplexity int, params model.MagicLoginInput) int
|
||||||
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
||||||
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
||||||
Signup func(childComplexity int, params model.SignUpInput) int
|
Signup func(childComplexity int, params model.SignUpInput) int
|
||||||
@@ -117,6 +119,7 @@ type ComplexityRoot struct {
|
|||||||
type MutationResolver interface {
|
type MutationResolver interface {
|
||||||
Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error)
|
Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error)
|
||||||
Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error)
|
Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error)
|
||||||
|
MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Response, error)
|
||||||
Logout(ctx context.Context) (*model.Response, error)
|
Logout(ctx context.Context) (*model.Response, error)
|
||||||
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
|
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
|
||||||
AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*model.User, error)
|
AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*model.User, error)
|
||||||
@@ -226,6 +229,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Meta.IsGoogleLoginEnabled(childComplexity), true
|
return e.complexity.Meta.IsGoogleLoginEnabled(childComplexity), true
|
||||||
|
|
||||||
|
case "Meta.isMagicLoginEnabled":
|
||||||
|
if e.complexity.Meta.IsMagicLoginEnabled == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Meta.IsMagicLoginEnabled(childComplexity), true
|
||||||
|
|
||||||
case "Meta.isTwitterLoginEnabled":
|
case "Meta.isTwitterLoginEnabled":
|
||||||
if e.complexity.Meta.IsTwitterLoginEnabled == nil {
|
if e.complexity.Meta.IsTwitterLoginEnabled == nil {
|
||||||
break
|
break
|
||||||
@@ -295,6 +305,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Mutation.Logout(childComplexity), true
|
return e.complexity.Mutation.Logout(childComplexity), true
|
||||||
|
|
||||||
|
case "Mutation.magicLogin":
|
||||||
|
if e.complexity.Mutation.MagicLogin == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_magicLogin_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.MagicLogin(childComplexity, args["params"].(model.MagicLoginInput)), true
|
||||||
|
|
||||||
case "Mutation.resendVerifyEmail":
|
case "Mutation.resendVerifyEmail":
|
||||||
if e.complexity.Mutation.ResendVerifyEmail == nil {
|
if e.complexity.Mutation.ResendVerifyEmail == nil {
|
||||||
break
|
break
|
||||||
@@ -600,6 +622,7 @@ type Meta {
|
|||||||
isGithubLoginEnabled: Boolean!
|
isGithubLoginEnabled: Boolean!
|
||||||
isEmailVerificationEnabled: Boolean!
|
isEmailVerificationEnabled: Boolean!
|
||||||
isBasicAuthenticationEnabled: Boolean!
|
isBasicAuthenticationEnabled: Boolean!
|
||||||
|
isMagicLoginEnabled: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
@@ -699,9 +722,15 @@ input DeleteUserInput {
|
|||||||
email: String!
|
email: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input MagicLoginInput {
|
||||||
|
email: String!
|
||||||
|
roles: [String!]
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
|
magicLogin(params: MagicLoginInput!): Response!
|
||||||
logout: Response!
|
logout: Response!
|
||||||
updateProfile(params: UpdateProfileInput!): Response!
|
updateProfile(params: UpdateProfileInput!): Response!
|
||||||
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
||||||
@@ -787,6 +816,21 @@ func (ec *executionContext) field_Mutation_login_args(ctx context.Context, rawAr
|
|||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_magicLogin_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 model.MagicLoginInput
|
||||||
|
if tmp, ok := rawArgs["params"]; ok {
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||||
|
arg0, err = ec.unmarshalNMagicLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMagicLoginInput(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["params"] = arg0
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_resendVerifyEmail_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_resendVerifyEmail_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
@@ -1376,6 +1420,41 @@ func (ec *executionContext) _Meta_isBasicAuthenticationEnabled(ctx context.Conte
|
|||||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Meta_isMagicLoginEnabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "Meta",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.IsMagicLoginEnabled, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(bool)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_signup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation_signup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -1460,6 +1539,48 @@ func (ec *executionContext) _Mutation_login(ctx context.Context, field graphql.C
|
|||||||
return ec.marshalNAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res)
|
return ec.marshalNAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_magicLogin(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
IsResolver: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
|
args, err := ec.field_Mutation_magicLogin_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
fc.Args = args
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Mutation().MagicLogin(rctx, args["params"].(model.MagicLoginInput))
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*model.Response)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_logout(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation_logout(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -3856,6 +3977,34 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
|
|||||||
return it, nil
|
return it, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalInputMagicLoginInput(ctx context.Context, obj interface{}) (model.MagicLoginInput, error) {
|
||||||
|
var it model.MagicLoginInput
|
||||||
|
var asMap = obj.(map[string]interface{})
|
||||||
|
|
||||||
|
for k, v := range asMap {
|
||||||
|
switch k {
|
||||||
|
case "email":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||||
|
it.Email, err = ec.unmarshalNString2string(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "roles":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||||
|
it.Roles, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputResendVerifyEmailInput(ctx context.Context, obj interface{}) (model.ResendVerifyEmailInput, error) {
|
func (ec *executionContext) unmarshalInputResendVerifyEmailInput(ctx context.Context, obj interface{}) (model.ResendVerifyEmailInput, error) {
|
||||||
var it model.ResendVerifyEmailInput
|
var it model.ResendVerifyEmailInput
|
||||||
var asMap = obj.(map[string]interface{})
|
var asMap = obj.(map[string]interface{})
|
||||||
@@ -4187,6 +4336,11 @@ func (ec *executionContext) _Meta(ctx context.Context, sel ast.SelectionSet, obj
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
case "isMagicLoginEnabled":
|
||||||
|
out.Values[i] = ec._Meta_isMagicLoginEnabled(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
@@ -4223,6 +4377,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
case "magicLogin":
|
||||||
|
out.Values[i] = ec._Mutation_magicLogin(ctx, field)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "logout":
|
case "logout":
|
||||||
out.Values[i] = ec._Mutation_logout(ctx, field)
|
out.Values[i] = ec._Mutation_logout(ctx, field)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
@@ -4800,6 +4959,11 @@ func (ec *executionContext) unmarshalNLoginInput2githubᚗcomᚋauthorizerdevᚋ
|
|||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNMagicLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMagicLoginInput(ctx context.Context, v interface{}) (model.MagicLoginInput, error) {
|
||||||
|
res, err := ec.unmarshalInputMagicLoginInput(ctx, v)
|
||||||
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalNMeta2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMeta(ctx context.Context, sel ast.SelectionSet, v model.Meta) graphql.Marshaler {
|
func (ec *executionContext) marshalNMeta2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMeta(ctx context.Context, sel ast.SelectionSet, v model.Meta) graphql.Marshaler {
|
||||||
return ec._Meta(ctx, sel, &v)
|
return ec._Meta(ctx, sel, &v)
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,11 @@ type LoginInput struct {
|
|||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MagicLoginInput struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Roles []string `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
IsGoogleLoginEnabled bool `json:"isGoogleLoginEnabled"`
|
IsGoogleLoginEnabled bool `json:"isGoogleLoginEnabled"`
|
||||||
@@ -45,6 +50,7 @@ type Meta struct {
|
|||||||
IsGithubLoginEnabled bool `json:"isGithubLoginEnabled"`
|
IsGithubLoginEnabled bool `json:"isGithubLoginEnabled"`
|
||||||
IsEmailVerificationEnabled bool `json:"isEmailVerificationEnabled"`
|
IsEmailVerificationEnabled bool `json:"isEmailVerificationEnabled"`
|
||||||
IsBasicAuthenticationEnabled bool `json:"isBasicAuthenticationEnabled"`
|
IsBasicAuthenticationEnabled bool `json:"isBasicAuthenticationEnabled"`
|
||||||
|
IsMagicLoginEnabled bool `json:"isMagicLoginEnabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResendVerifyEmailInput struct {
|
type ResendVerifyEmailInput struct {
|
||||||
|
@@ -13,6 +13,7 @@ type Meta {
|
|||||||
isGithubLoginEnabled: Boolean!
|
isGithubLoginEnabled: Boolean!
|
||||||
isEmailVerificationEnabled: Boolean!
|
isEmailVerificationEnabled: Boolean!
|
||||||
isBasicAuthenticationEnabled: Boolean!
|
isBasicAuthenticationEnabled: Boolean!
|
||||||
|
isMagicLoginEnabled: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
@@ -112,9 +113,15 @@ input DeleteUserInput {
|
|||||||
email: String!
|
email: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input MagicLoginInput {
|
||||||
|
email: String!
|
||||||
|
roles: [String!]
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
|
magicLogin(params: MagicLoginInput!): Response!
|
||||||
logout: Response!
|
logout: Response!
|
||||||
updateProfile(params: UpdateProfileInput!): Response!
|
updateProfile(params: UpdateProfileInput!): Response!
|
||||||
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
adminUpdateUser(params: AdminUpdateUserInput!): User!
|
||||||
|
@@ -19,6 +19,10 @@ func (r *mutationResolver) Login(ctx context.Context, params model.LoginInput) (
|
|||||||
return resolvers.Login(ctx, params)
|
return resolvers.Login(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Response, error) {
|
||||||
|
return resolvers.MagicLogin(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) Logout(ctx context.Context) (*model.Response, error) {
|
func (r *mutationResolver) Logout(ctx context.Context) (*model.Response, error) {
|
||||||
return resolvers.Logout(ctx)
|
return resolvers.Logout(ctx)
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -15,36 +16,50 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/oauth"
|
"github.com/authorizerdev/authorizer/server/oauth"
|
||||||
"github.com/authorizerdev/authorizer/server/session"
|
"github.com/authorizerdev/authorizer/server/session"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func processGoogleUserInfo(code string) (db.User, error) {
|
func processGoogleUserInfo(code string) (db.User, error) {
|
||||||
user := db.User{}
|
user := db.User{}
|
||||||
token, err := oauth.OAuthProvider.GoogleConfig.Exchange(oauth2.NoContext, code)
|
ctx := context.Background()
|
||||||
|
oauth2Token, err := oauth.OAuthProviders.GoogleConfig.Exchange(ctx, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, fmt.Errorf("invalid google exchange code: %s", err.Error())
|
return user, fmt.Errorf("invalid google exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
client := oauth.OAuthProvider.GoogleConfig.Client(oauth2.NoContext, token)
|
|
||||||
response, err := client.Get(constants.GoogleUserInfoURL)
|
verifier := oauth.OIDCProviders.GoogleOIDC.Verifier(&oidc.Config{ClientID: oauth.OAuthProviders.GoogleConfig.ClientID})
|
||||||
if err != nil {
|
|
||||||
return user, err
|
// Extract the ID Token from OAuth2 token.
|
||||||
|
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||||
|
if !ok {
|
||||||
|
return user, fmt.Errorf("unable to extract id_token")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
// Parse and verify ID Token payload.
|
||||||
body, err := ioutil.ReadAll(response.Body)
|
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, fmt.Errorf("failed to read google response body: %s", err.Error())
|
return user, fmt.Errorf("unable to verify id_token:", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
userRawData := make(map[string]string)
|
// Extract custom claims
|
||||||
json.Unmarshal(body, &userRawData)
|
var claims struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
GivenName string `json:"given_name"`
|
||||||
|
FamilyName string `json:"family_name"`
|
||||||
|
Verified bool `json:"email_verified"`
|
||||||
|
}
|
||||||
|
if err := idToken.Claims(&claims); err != nil {
|
||||||
|
return user, fmt.Errorf("unable to extract claims")
|
||||||
|
}
|
||||||
|
|
||||||
user = db.User{
|
user = db.User{
|
||||||
FirstName: userRawData["given_name"],
|
FirstName: claims.GivenName,
|
||||||
LastName: userRawData["family_name"],
|
LastName: claims.FamilyName,
|
||||||
Image: userRawData["picture"],
|
Image: claims.Picture,
|
||||||
Email: userRawData["email"],
|
Email: claims.Email,
|
||||||
EmailVerifiedAt: time.Now().Unix(),
|
EmailVerifiedAt: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +68,7 @@ func processGoogleUserInfo(code string) (db.User, error) {
|
|||||||
|
|
||||||
func processGithubUserInfo(code string) (db.User, error) {
|
func processGithubUserInfo(code string) (db.User, error) {
|
||||||
user := db.User{}
|
user := db.User{}
|
||||||
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
|
token, err := oauth.OAuthProviders.GithubConfig.Exchange(oauth2.NoContext, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, fmt.Errorf("invalid github exchange code: %s", err.Error())
|
return user, fmt.Errorf("invalid github exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -102,7 +117,7 @@ func processGithubUserInfo(code string) (db.User, error) {
|
|||||||
|
|
||||||
func processFacebookUserInfo(code string) (db.User, error) {
|
func processFacebookUserInfo(code string) (db.User, error) {
|
||||||
user := db.User{}
|
user := db.User{}
|
||||||
token, err := oauth.OAuthProvider.FacebookConfig.Exchange(oauth2.NoContext, code)
|
token, err := oauth.OAuthProviders.FacebookConfig.Exchange(oauth2.NoContext, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -147,11 +162,11 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
provider := c.Param("oauth_provider")
|
provider := c.Param("oauth_provider")
|
||||||
state := c.Request.FormValue("state")
|
state := c.Request.FormValue("state")
|
||||||
|
|
||||||
sessionState := session.GetToken(state)
|
sessionState := session.GetSocailLoginState(state)
|
||||||
if sessionState == "" {
|
if sessionState == "" {
|
||||||
c.JSON(400, gin.H{"error": "invalid oauth state"})
|
c.JSON(400, gin.H{"error": "invalid oauth state"})
|
||||||
}
|
}
|
||||||
session.DeleteToken(sessionState)
|
session.RemoveSocialLoginState(state)
|
||||||
// contains random token, redirect url, role
|
// contains random token, redirect url, role
|
||||||
sessionSplit := strings.Split(state, "___")
|
sessionSplit := strings.Split(state, "___")
|
||||||
|
|
||||||
@@ -208,7 +223,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
signupMethod := existingUser.SignupMethod
|
signupMethod := existingUser.SignupMethod
|
||||||
if !strings.Contains(signupMethod, provider) {
|
if !strings.Contains(signupMethod, provider) {
|
||||||
signupMethod = signupMethod + "," + enum.Github.String()
|
signupMethod = signupMethod + "," + provider
|
||||||
}
|
}
|
||||||
user.SignupMethod = signupMethod
|
user.SignupMethod = signupMethod
|
||||||
user.Password = existingUser.Password
|
user.Password = existingUser.Password
|
||||||
@@ -254,7 +269,16 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, inputRoles)
|
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, inputRoles)
|
||||||
utils.SetCookie(c, accessToken)
|
utils.SetCookie(c, accessToken)
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, accessToken, refreshToken)
|
||||||
|
go func() {
|
||||||
|
sessionData := db.Session{
|
||||||
|
UserID: user.ID,
|
||||||
|
UserAgent: utils.GetUserAgent(c.Request),
|
||||||
|
IP: utils.GetIP(c.Request),
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Mgr.SaveSession(sessionData)
|
||||||
|
}()
|
||||||
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||||
}
|
}
|
||||||
|
@@ -48,28 +48,46 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
oauthStateString := uuid.String() + "___" + redirectURL + "___" + roles
|
oauthStateString := uuid.String() + "___" + redirectURL + "___" + roles
|
||||||
|
|
||||||
provider := c.Param("oauth_provider")
|
provider := c.Param("oauth_provider")
|
||||||
|
isProviderConfigured := true
|
||||||
switch provider {
|
switch provider {
|
||||||
case enum.Google.String():
|
case enum.Google.String():
|
||||||
session.SetToken(oauthStateString, enum.Google.String())
|
if oauth.OAuthProviders.GoogleConfig == nil {
|
||||||
|
isProviderConfigured = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
session.SetSocailLoginState(oauthStateString, enum.Google.String())
|
||||||
// during the init of OAuthProvider authorizer url might be empty
|
// during the init of OAuthProvider authorizer url might be empty
|
||||||
oauth.OAuthProvider.GoogleConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/google"
|
oauth.OAuthProviders.GoogleConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/google"
|
||||||
url := oauth.OAuthProvider.GoogleConfig.AuthCodeURL(oauthStateString)
|
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
case enum.Github.String():
|
case enum.Github.String():
|
||||||
session.SetToken(oauthStateString, enum.Github.String())
|
if oauth.OAuthProviders.GithubConfig == nil {
|
||||||
oauth.OAuthProvider.GithubConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/github"
|
isProviderConfigured = false
|
||||||
url := oauth.OAuthProvider.GithubConfig.AuthCodeURL(oauthStateString)
|
break
|
||||||
|
}
|
||||||
|
session.SetSocailLoginState(oauthStateString, enum.Github.String())
|
||||||
|
oauth.OAuthProviders.GithubConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/github"
|
||||||
|
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
case enum.Facebook.String():
|
case enum.Facebook.String():
|
||||||
session.SetToken(oauthStateString, enum.Github.String())
|
if oauth.OAuthProviders.FacebookConfig == nil {
|
||||||
oauth.OAuthProvider.FacebookConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/facebook"
|
isProviderConfigured = false
|
||||||
url := oauth.OAuthProvider.FacebookConfig.AuthCodeURL(oauthStateString)
|
break
|
||||||
|
}
|
||||||
|
session.SetSocailLoginState(oauthStateString, enum.Facebook.String())
|
||||||
|
oauth.OAuthProviders.FacebookConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/facebook"
|
||||||
|
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
default:
|
default:
|
||||||
c.JSON(422, gin.H{
|
c.JSON(422, gin.H{
|
||||||
"message": "Invalid oauth provider",
|
"message": "Invalid oauth provider",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isProviderConfigured {
|
||||||
|
c.JSON(422, gin.H{
|
||||||
|
"message": provider + " not configured",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,9 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update email_verified_at in users table
|
// update email_verified_at in users table
|
||||||
|
if user.EmailVerifiedAt <= 0 {
|
||||||
db.Mgr.UpdateVerificationTime(time.Now().Unix(), user.ID)
|
db.Mgr.UpdateVerificationTime(time.Now().Unix(), user.ID)
|
||||||
|
}
|
||||||
// delete from verification table
|
// delete from verification table
|
||||||
db.Mgr.DeleteToken(claim.Email)
|
db.Mgr.DeleteToken(claim.Email)
|
||||||
|
|
||||||
@@ -56,8 +58,17 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, accessToken, refreshToken)
|
||||||
|
go func() {
|
||||||
|
sessionData := db.Session{
|
||||||
|
UserID: user.ID,
|
||||||
|
UserAgent: utils.GetUserAgent(c.Request),
|
||||||
|
IP: utils.GetIP(c.Request),
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Mgr.SaveSession(sessionData)
|
||||||
|
}()
|
||||||
utils.SetCookie(c, accessToken)
|
utils.SetCookie(c, accessToken)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, claim.Host)
|
c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,8 @@ func GinContextToContextMiddleware() gin.HandlerFunc {
|
|||||||
func CORSMiddleware() gin.HandlerFunc {
|
func CORSMiddleware() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
origin := c.Request.Header.Get("Origin")
|
origin := c.Request.Header.Get("Origin")
|
||||||
|
constants.APP_URL = origin
|
||||||
|
log.Println("=> APP_URL:", constants.APP_URL)
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||||
|
@@ -1,33 +1,49 @@
|
|||||||
package oauth
|
package oauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
facebookOAuth2 "golang.org/x/oauth2/facebook"
|
facebookOAuth2 "golang.org/x/oauth2/facebook"
|
||||||
githubOAuth2 "golang.org/x/oauth2/github"
|
githubOAuth2 "golang.org/x/oauth2/github"
|
||||||
googleOAuth2 "golang.org/x/oauth2/google"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type OAuthProviders struct {
|
type OAuthProvider struct {
|
||||||
GoogleConfig *oauth2.Config
|
GoogleConfig *oauth2.Config
|
||||||
GithubConfig *oauth2.Config
|
GithubConfig *oauth2.Config
|
||||||
FacebookConfig *oauth2.Config
|
FacebookConfig *oauth2.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
var OAuthProvider OAuthProviders
|
type OIDCProvider struct {
|
||||||
|
GoogleOIDC *oidc.Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
OAuthProviders OAuthProvider
|
||||||
|
OIDCProviders OIDCProvider
|
||||||
|
)
|
||||||
|
|
||||||
func InitOAuth() {
|
func InitOAuth() {
|
||||||
|
ctx := context.Background()
|
||||||
if constants.GOOGLE_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "" {
|
if constants.GOOGLE_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "" {
|
||||||
OAuthProvider.GoogleConfig = &oauth2.Config{
|
p, err := oidc.NewProvider(ctx, "https://accounts.google.com")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("error creating oidc provider for google:", err)
|
||||||
|
}
|
||||||
|
OIDCProviders.GoogleOIDC = p
|
||||||
|
OAuthProviders.GoogleConfig = &oauth2.Config{
|
||||||
ClientID: constants.GOOGLE_CLIENT_ID,
|
ClientID: constants.GOOGLE_CLIENT_ID,
|
||||||
ClientSecret: constants.GOOGLE_CLIENT_SECRET,
|
ClientSecret: constants.GOOGLE_CLIENT_SECRET,
|
||||||
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/google",
|
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/google",
|
||||||
Endpoint: googleOAuth2.Endpoint,
|
Endpoint: OIDCProviders.GoogleOIDC.Endpoint(),
|
||||||
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
|
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constants.GITHUB_CLIENT_ID != "" && constants.GITHUB_CLIENT_SECRET != "" {
|
if constants.GITHUB_CLIENT_ID != "" && constants.GITHUB_CLIENT_SECRET != "" {
|
||||||
OAuthProvider.GithubConfig = &oauth2.Config{
|
OAuthProviders.GithubConfig = &oauth2.Config{
|
||||||
ClientID: constants.GITHUB_CLIENT_ID,
|
ClientID: constants.GITHUB_CLIENT_ID,
|
||||||
ClientSecret: constants.GITHUB_CLIENT_SECRET,
|
ClientSecret: constants.GITHUB_CLIENT_SECRET,
|
||||||
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/github",
|
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/github",
|
||||||
@@ -35,7 +51,7 @@ func InitOAuth() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "" {
|
if constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "" {
|
||||||
OAuthProvider.FacebookConfig = &oauth2.Config{
|
OAuthProviders.FacebookConfig = &oauth2.Config{
|
||||||
ClientID: constants.FACEBOOK_CLIENT_ID,
|
ClientID: constants.FACEBOOK_CLIENT_ID,
|
||||||
ClientSecret: constants.FACEBOOK_CLIENT_SECRET,
|
ClientSecret: constants.FACEBOOK_CLIENT_SECRET,
|
||||||
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/facebook",
|
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/facebook",
|
||||||
|
@@ -60,7 +60,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
|
|||||||
return res, fmt.Errorf("user with this email address already exists")
|
return res, fmt.Errorf("user with this email address already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
session.DeleteUserSession(fmt.Sprintf("%v", user.ID))
|
||||||
utils.DeleteCookie(gc)
|
utils.DeleteCookie(gc)
|
||||||
|
|
||||||
user.Email = newEmail
|
user.Email = newEmail
|
||||||
@@ -100,7 +100,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
|
|||||||
rolesToSave = strings.Join(inputRoles, ",")
|
rolesToSave = strings.Join(inputRoles, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
session.DeleteUserSession(fmt.Sprintf("%v", user.ID))
|
||||||
utils.DeleteCookie(gc)
|
utils.DeleteCookie(gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,7 +27,7 @@ func DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Respo
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
session.DeleteToken(fmt.Sprintf("%x", user.ID))
|
session.DeleteUserSession(fmt.Sprintf("%x", user.ID))
|
||||||
|
|
||||||
err = db.Mgr.DeleteUser(params.Email)
|
err = db.Mgr.DeleteUser(params.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -60,7 +60,16 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
|||||||
|
|
||||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, accessToken, refreshToken)
|
||||||
|
go func() {
|
||||||
|
sessionData := db.Session{
|
||||||
|
UserID: user.ID,
|
||||||
|
UserAgent: utils.GetUserAgent(gc.Request),
|
||||||
|
IP: utils.GetIP(gc.Request),
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Mgr.SaveSession(sessionData)
|
||||||
|
}()
|
||||||
|
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Logged in successfully`,
|
Message: `Logged in successfully`,
|
||||||
|
@@ -27,7 +27,7 @@ func Logout(ctx context.Context) (*model.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userId := fmt.Sprintf("%v", claim["id"])
|
userId := fmt.Sprintf("%v", claim["id"])
|
||||||
session.DeleteToken(userId)
|
session.DeleteToken(userId, token)
|
||||||
res = &model.Response{
|
res = &model.Response{
|
||||||
Message: "Logged out successfully",
|
Message: "Logged out successfully",
|
||||||
}
|
}
|
||||||
|
122
server/resolvers/magicLogin.go
Normal file
122
server/resolvers/magicLogin.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
package resolvers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
|
"github.com/authorizerdev/authorizer/server/enum"
|
||||||
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Response, error) {
|
||||||
|
var res *model.Response
|
||||||
|
|
||||||
|
if constants.DISABLE_MAGIC_LOGIN == "true" {
|
||||||
|
return res, fmt.Errorf(`magic link login is disabled for this instance`)
|
||||||
|
}
|
||||||
|
|
||||||
|
params.Email = strings.ToLower(params.Email)
|
||||||
|
|
||||||
|
if !utils.IsValidEmail(params.Email) {
|
||||||
|
return res, fmt.Errorf(`invalid email address`)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputRoles := []string{}
|
||||||
|
|
||||||
|
user := db.User{
|
||||||
|
Email: params.Email,
|
||||||
|
}
|
||||||
|
|
||||||
|
// find user with email
|
||||||
|
existingUser, err := db.Mgr.GetUserByEmail(params.Email)
|
||||||
|
if err != nil {
|
||||||
|
user.SignupMethod = enum.MagicLink.String()
|
||||||
|
// define roles for new user
|
||||||
|
if len(params.Roles) > 0 {
|
||||||
|
// check if roles exists
|
||||||
|
if !utils.IsValidRoles(constants.ROLES, params.Roles) {
|
||||||
|
return res, fmt.Errorf(`invalid roles`)
|
||||||
|
} else {
|
||||||
|
inputRoles = params.Roles
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inputRoles = constants.DEFAULT_ROLES
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Roles = strings.Join(inputRoles, ",")
|
||||||
|
} else {
|
||||||
|
user = existingUser
|
||||||
|
// There multiple scenarios with roles here in magic link login
|
||||||
|
// 1. user has access to protected roles + roles and trying to login
|
||||||
|
// 2. user has not signed up for one of the available role but trying to signup.
|
||||||
|
// Need to modify roles in this case
|
||||||
|
|
||||||
|
// find the unassigned roles
|
||||||
|
existingRoles := strings.Split(existingUser.Roles, ",")
|
||||||
|
unasignedRoles := []string{}
|
||||||
|
for _, ir := range inputRoles {
|
||||||
|
if !utils.StringSliceContains(existingRoles, ir) {
|
||||||
|
unasignedRoles = append(unasignedRoles, ir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(unasignedRoles) > 0 {
|
||||||
|
// check if it contains protected unassigned role
|
||||||
|
hasProtectedRole := false
|
||||||
|
for _, ur := range unasignedRoles {
|
||||||
|
if utils.StringSliceContains(constants.PROTECTED_ROLES, ur) {
|
||||||
|
hasProtectedRole = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasProtectedRole {
|
||||||
|
return res, fmt.Errorf(`invalid roles`)
|
||||||
|
} else {
|
||||||
|
user.Roles = existingUser.Roles + "," + strings.Join(unasignedRoles, ",")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user.Roles = existingUser.Roles
|
||||||
|
}
|
||||||
|
|
||||||
|
signupMethod := existingUser.SignupMethod
|
||||||
|
if !strings.Contains(signupMethod, enum.MagicLink.String()) {
|
||||||
|
signupMethod = signupMethod + "," + enum.MagicLink.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
user.SignupMethod = signupMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
user, _ = db.Mgr.SaveUser(user)
|
||||||
|
|
||||||
|
if constants.DISABLE_EMAIL_VERIFICATION != "true" {
|
||||||
|
// insert verification request
|
||||||
|
verificationType := enum.MagicLink.String()
|
||||||
|
token, err := utils.CreateVerificationToken(params.Email, verificationType)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(`Error generating token`, err)
|
||||||
|
}
|
||||||
|
db.Mgr.AddVerification(db.VerificationRequest{
|
||||||
|
Token: token,
|
||||||
|
Identifier: verificationType,
|
||||||
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
|
Email: params.Email,
|
||||||
|
})
|
||||||
|
|
||||||
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
go func() {
|
||||||
|
utils.SendVerificationMail(params.Email, token)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
res = &model.Response{
|
||||||
|
Message: `Magic Link has been sent to your email. Please check your inbox!`,
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
@@ -30,7 +30,7 @@ func Profile(ctx context.Context) (*model.User, error) {
|
|||||||
|
|
||||||
userID := fmt.Sprintf("%v", claim["id"])
|
userID := fmt.Sprintf("%v", claim["id"])
|
||||||
email := fmt.Sprintf("%v", claim["email"])
|
email := fmt.Sprintf("%v", claim["email"])
|
||||||
sessionToken := session.GetToken(userID)
|
sessionToken := session.GetToken(userID, token)
|
||||||
|
|
||||||
if sessionToken == "" {
|
if sessionToken == "" {
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
@@ -127,7 +127,16 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
|||||||
|
|
||||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, accessToken, refreshToken)
|
||||||
|
go func() {
|
||||||
|
sessionData := db.Session{
|
||||||
|
UserID: user.ID,
|
||||||
|
UserAgent: utils.GetUserAgent(gc.Request),
|
||||||
|
IP: utils.GetIP(gc.Request),
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Mgr.SaveSession(sessionData)
|
||||||
|
}()
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Signed up successfully.`,
|
Message: `Signed up successfully.`,
|
||||||
AccessToken: &accessToken,
|
AccessToken: &accessToken,
|
||||||
|
@@ -37,7 +37,7 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
|||||||
|
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||||
|
|
||||||
sessionToken := session.GetToken(userIdStr)
|
sessionToken := session.GetToken(userIdStr, token)
|
||||||
|
|
||||||
if sessionToken == "" {
|
if sessionToken == "" {
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
@@ -63,7 +63,19 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
|||||||
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
|
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
|
||||||
// if access token has expired and refresh/session token is valid
|
// if access token has expired and refresh/session token is valid
|
||||||
// generate new accessToken
|
// generate new accessToken
|
||||||
|
currentRefreshToken := session.GetToken(userIdStr, token)
|
||||||
|
session.DeleteToken(userIdStr, token)
|
||||||
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles)
|
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles)
|
||||||
|
session.SetToken(userIdStr, token, currentRefreshToken)
|
||||||
|
go func() {
|
||||||
|
sessionData := db.Session{
|
||||||
|
UserID: user.ID,
|
||||||
|
UserAgent: utils.GetUserAgent(gc.Request),
|
||||||
|
IP: utils.GetIP(gc.Request),
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Mgr.SaveSession(sessionData)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.SetCookie(gc, token)
|
utils.SetCookie(gc, token)
|
||||||
@@ -80,6 +92,8 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
|||||||
Roles: strings.Split(user.Roles, ","),
|
Roles: strings.Split(user.Roles, ","),
|
||||||
CreatedAt: &user.CreatedAt,
|
CreatedAt: &user.CreatedAt,
|
||||||
UpdatedAt: &user.UpdatedAt,
|
UpdatedAt: &user.UpdatedAt,
|
||||||
|
SignupMethod: user.SignupMethod,
|
||||||
|
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
|
@@ -33,7 +33,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
|||||||
}
|
}
|
||||||
|
|
||||||
id := fmt.Sprintf("%v", claim["id"])
|
id := fmt.Sprintf("%v", claim["id"])
|
||||||
sessionToken := session.GetToken(id)
|
sessionToken := session.GetToken(id, token)
|
||||||
|
|
||||||
if sessionToken == "" {
|
if sessionToken == "" {
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
@@ -99,7 +99,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
|||||||
return res, fmt.Errorf("user with this email address already exists")
|
return res, fmt.Errorf("user with this email address already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
session.DeleteUserSession(fmt.Sprintf("%v", user.ID))
|
||||||
utils.DeleteCookie(gc)
|
utils.DeleteCookie(gc)
|
||||||
|
|
||||||
user.Email = newEmail
|
user.Email = newEmail
|
||||||
|
@@ -47,7 +47,16 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
|||||||
|
|
||||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, accessToken, refreshToken)
|
||||||
|
go func() {
|
||||||
|
sessionData := db.Session{
|
||||||
|
UserID: user.ID,
|
||||||
|
UserAgent: utils.GetUserAgent(gc.Request),
|
||||||
|
IP: utils.GetIP(gc.Request),
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Mgr.SaveSession(sessionData)
|
||||||
|
}()
|
||||||
|
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Email verified successfully.`,
|
Message: `Email verified successfully.`,
|
||||||
|
@@ -1,41 +1,88 @@
|
|||||||
package session
|
package session
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
type InMemoryStore struct {
|
type InMemoryStore struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
store map[string]string
|
store map[string]map[string]string
|
||||||
|
socialLoginState map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InMemoryStore) AddToken(userId, token string) {
|
func (c *InMemoryStore) AddToken(userId, accessToken, refreshToken string) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
// delete sessions > 500 // not recommended for production
|
// delete sessions > 500 // not recommended for production
|
||||||
if len(c.store) >= 500 {
|
if len(c.store) >= 500 {
|
||||||
c.store = make(map[string]string)
|
c.store = map[string]map[string]string{}
|
||||||
}
|
}
|
||||||
c.store[userId] = token
|
// check if entry exists in map
|
||||||
|
_, exists := c.store[userId]
|
||||||
|
if exists {
|
||||||
|
tempMap := c.store[userId]
|
||||||
|
tempMap[accessToken] = refreshToken
|
||||||
|
c.store[userId] = tempMap
|
||||||
|
} else {
|
||||||
|
tempMap := map[string]string{
|
||||||
|
accessToken: refreshToken,
|
||||||
|
}
|
||||||
|
c.store[userId] = tempMap
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(c.store)
|
||||||
|
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InMemoryStore) DeleteToken(userId string) {
|
func (c *InMemoryStore) DeleteUserSession(userId string) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
delete(c.store, userId)
|
delete(c.store, userId)
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InMemoryStore) ClearStore() {
|
func (c *InMemoryStore) DeleteToken(userId, accessToken string) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c.store = make(map[string]string)
|
delete(c.store[userId], accessToken)
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InMemoryStore) GetToken(userId string) string {
|
func (c *InMemoryStore) ClearStore() {
|
||||||
|
c.mu.Lock()
|
||||||
|
c.store = map[string]map[string]string{}
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InMemoryStore) GetToken(userId, accessToken string) string {
|
||||||
token := ""
|
token := ""
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
if val, ok := c.store[userId]; ok {
|
if sessionMap, ok := c.store[userId]; ok {
|
||||||
|
if val, ok := sessionMap[accessToken]; ok {
|
||||||
token = val
|
token = val
|
||||||
}
|
}
|
||||||
|
}
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *InMemoryStore) SetSocialLoginState(key, state string) {
|
||||||
|
c.mu.Lock()
|
||||||
|
c.socialLoginState[key] = state
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InMemoryStore) GetSocialLoginState(key string) string {
|
||||||
|
state := ""
|
||||||
|
if stateVal, ok := c.socialLoginState[key]; ok {
|
||||||
|
state = stateVal
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InMemoryStore) RemoveSocialLoginState(key string) {
|
||||||
|
c.mu.Lock()
|
||||||
|
delete(c.socialLoginState, key)
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package session
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
@@ -12,20 +13,29 @@ type RedisStore struct {
|
|||||||
store *redis.Client
|
store *redis.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RedisStore) AddToken(userId, token string) {
|
func (c *RedisStore) AddToken(userId, accessToken, refreshToken string) {
|
||||||
err := c.store.Set(c.ctx, "authorizer_"+userId, token, 0).Err()
|
err := c.store.HMSet(c.ctx, "authorizer_"+userId, map[string]string{
|
||||||
|
accessToken: refreshToken,
|
||||||
|
}).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Error saving redis token:", err)
|
log.Fatalln("Error saving redis token:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RedisStore) DeleteToken(userId string) {
|
func (c *RedisStore) DeleteUserSession(userId string) {
|
||||||
err := c.store.Del(c.ctx, "authorizer_"+userId).Err()
|
err := c.store.Del(c.ctx, "authorizer_"+userId).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Error deleting redis token:", err)
|
log.Fatalln("Error deleting redis token:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *RedisStore) DeleteToken(userId, accessToken string) {
|
||||||
|
err := c.store.HDel(c.ctx, "authorizer_"+userId, accessToken).Err()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Error deleting redis token:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *RedisStore) ClearStore() {
|
func (c *RedisStore) ClearStore() {
|
||||||
err := c.store.Del(c.ctx, "authorizer_*").Err()
|
err := c.store.Del(c.ctx, "authorizer_*").Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,11 +43,38 @@ func (c *RedisStore) ClearStore() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RedisStore) GetToken(userId string) string {
|
func (c *RedisStore) GetToken(userId, accessToken string) string {
|
||||||
token := ""
|
token := ""
|
||||||
token, err := c.store.Get(c.ctx, "authorizer_"+userId).Result()
|
res, err := c.store.HMGet(c.ctx, "authorizer_"+userId, accessToken).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error getting token from redis store:", err)
|
log.Println("Error getting token from redis store:", err)
|
||||||
}
|
}
|
||||||
|
if len(res) > 0 && res[0] != nil {
|
||||||
|
token = fmt.Sprintf("%v", res[0])
|
||||||
|
}
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *RedisStore) SetSocialLoginState(key, state string) {
|
||||||
|
err := c.store.Set(c.ctx, key, state, 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Error saving redis token:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RedisStore) GetSocialLoginState(key string) string {
|
||||||
|
state := ""
|
||||||
|
state, err := c.store.Get(c.ctx, key).Result()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error getting token from redis store:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RedisStore) RemoveSocialLoginState(key string) {
|
||||||
|
err := c.store.Del(c.ctx, key).Err()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Error deleting redis token:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -15,30 +15,42 @@ type SessionStore struct {
|
|||||||
|
|
||||||
var SessionStoreObj SessionStore
|
var SessionStoreObj SessionStore
|
||||||
|
|
||||||
func SetToken(userId, token string) {
|
func SetToken(userId, accessToken, refreshToken string) {
|
||||||
|
// TODO: Set session information in db for all the sessions that gets generated
|
||||||
|
// it should async go function
|
||||||
|
|
||||||
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
||||||
SessionStoreObj.RedisMemoryStoreObj.AddToken(userId, token)
|
SessionStoreObj.RedisMemoryStoreObj.AddToken(userId, accessToken, refreshToken)
|
||||||
}
|
}
|
||||||
if SessionStoreObj.InMemoryStoreObj != nil {
|
if SessionStoreObj.InMemoryStoreObj != nil {
|
||||||
SessionStoreObj.InMemoryStoreObj.AddToken(userId, token)
|
SessionStoreObj.InMemoryStoreObj.AddToken(userId, accessToken, refreshToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteToken(userId string) {
|
func DeleteToken(userId, accessToken string) {
|
||||||
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
||||||
SessionStoreObj.RedisMemoryStoreObj.DeleteToken(userId)
|
SessionStoreObj.RedisMemoryStoreObj.DeleteToken(userId, accessToken)
|
||||||
}
|
}
|
||||||
if SessionStoreObj.InMemoryStoreObj != nil {
|
if SessionStoreObj.InMemoryStoreObj != nil {
|
||||||
SessionStoreObj.InMemoryStoreObj.DeleteToken(userId)
|
SessionStoreObj.InMemoryStoreObj.DeleteToken(userId, accessToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetToken(userId string) string {
|
func DeleteUserSession(userId string) {
|
||||||
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
||||||
return SessionStoreObj.RedisMemoryStoreObj.GetToken(userId)
|
SessionStoreObj.RedisMemoryStoreObj.DeleteUserSession(userId)
|
||||||
}
|
}
|
||||||
if SessionStoreObj.InMemoryStoreObj != nil {
|
if SessionStoreObj.InMemoryStoreObj != nil {
|
||||||
return SessionStoreObj.InMemoryStoreObj.GetToken(userId)
|
SessionStoreObj.InMemoryStoreObj.DeleteUserSession(userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetToken(userId, accessToken string) string {
|
||||||
|
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
||||||
|
return SessionStoreObj.RedisMemoryStoreObj.GetToken(userId, accessToken)
|
||||||
|
}
|
||||||
|
if SessionStoreObj.InMemoryStoreObj != nil {
|
||||||
|
return SessionStoreObj.InMemoryStoreObj.GetToken(userId, accessToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
@@ -53,6 +65,35 @@ func ClearStore() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetSocailLoginState(key, state string) {
|
||||||
|
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
||||||
|
SessionStoreObj.RedisMemoryStoreObj.SetSocialLoginState(key, state)
|
||||||
|
}
|
||||||
|
if SessionStoreObj.InMemoryStoreObj != nil {
|
||||||
|
SessionStoreObj.InMemoryStoreObj.SetSocialLoginState(key, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSocailLoginState(key string) string {
|
||||||
|
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
||||||
|
return SessionStoreObj.RedisMemoryStoreObj.GetSocialLoginState(key)
|
||||||
|
}
|
||||||
|
if SessionStoreObj.InMemoryStoreObj != nil {
|
||||||
|
return SessionStoreObj.InMemoryStoreObj.GetSocialLoginState(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveSocialLoginState(key string) {
|
||||||
|
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
||||||
|
SessionStoreObj.RedisMemoryStoreObj.RemoveSocialLoginState(key)
|
||||||
|
}
|
||||||
|
if SessionStoreObj.InMemoryStoreObj != nil {
|
||||||
|
SessionStoreObj.InMemoryStoreObj.RemoveSocialLoginState(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func InitSession() {
|
func InitSession() {
|
||||||
if constants.REDIS_URL != "" {
|
if constants.REDIS_URL != "" {
|
||||||
log.Println("Using redis store to save sessions")
|
log.Println("Using redis store to save sessions")
|
||||||
@@ -75,7 +116,8 @@ func InitSession() {
|
|||||||
} else {
|
} else {
|
||||||
log.Println("Using in memory store to save sessions")
|
log.Println("Using in memory store to save sessions")
|
||||||
SessionStoreObj.InMemoryStoreObj = &InMemoryStore{
|
SessionStoreObj.InMemoryStoreObj = &InMemoryStore{
|
||||||
store: make(map[string]string),
|
store: map[string]map[string]string{},
|
||||||
|
socialLoginState: map[string]string{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/enum"
|
"github.com/authorizerdev/authorizer/server/enum"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
v8 "rogchap.com/v8go"
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) {
|
func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) {
|
||||||
@@ -50,25 +50,24 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
|
|||||||
"signUpMethods": strings.Split(user.SignupMethod, ","),
|
"signUpMethods": strings.Split(user.SignupMethod, ","),
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, _ := v8.NewContext()
|
vm := otto.New()
|
||||||
userBytes, _ := json.Marshal(userInfo)
|
userBytes, _ := json.Marshal(userInfo)
|
||||||
claimBytes, _ := json.Marshal(customClaims)
|
claimBytes, _ := json.Marshal(customClaims)
|
||||||
|
|
||||||
ctx.RunScript(fmt.Sprintf(`
|
vm.Run(fmt.Sprintf(`
|
||||||
const user = %s;
|
var user = %s;
|
||||||
const tokenPayload = %s;
|
var tokenPayload = %s;
|
||||||
const customFunction = %s;
|
var customFunction = %s;
|
||||||
const functionRes = JSON.stringify(customFunction(user, tokenPayload));
|
var functionRes = JSON.stringify(customFunction(user, tokenPayload));
|
||||||
`, string(userBytes), string(claimBytes), accessTokenScript), "functionCall.js")
|
`, string(userBytes), string(claimBytes), accessTokenScript))
|
||||||
|
|
||||||
val, err := ctx.RunScript("functionRes", "functionRes.js")
|
val, err := vm.Get("functionRes")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("=> err custom access token script:", err)
|
log.Println("=> err custom access token script:", err)
|
||||||
} else {
|
} else {
|
||||||
extraPayload := make(map[string]interface{})
|
extraPayload := make(map[string]interface{})
|
||||||
err = json.Unmarshal([]byte(fmt.Sprintf("%s", val)), &extraPayload)
|
err = json.Unmarshal([]byte(fmt.Sprintf("%s", val)), &extraPayload)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error converting accessTokenScript response to map:", err)
|
log.Println("Error converting accessTokenScript response to map:", err)
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
@@ -11,9 +10,8 @@ import (
|
|||||||
func SetCookie(gc *gin.Context, token string) {
|
func SetCookie(gc *gin.Context, token string) {
|
||||||
secure := true
|
secure := true
|
||||||
httpOnly := true
|
httpOnly := true
|
||||||
|
|
||||||
host := GetHostName(constants.AUTHORIZER_URL)
|
host := GetHostName(constants.AUTHORIZER_URL)
|
||||||
log.Println("=> cookie host", host)
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
gc.SetSameSite(http.SameSiteNoneMode)
|
||||||
gc.SetCookie(constants.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
|
gc.SetCookie(constants.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
|
||||||
}
|
}
|
||||||
@@ -31,11 +29,8 @@ func DeleteCookie(gc *gin.Context) {
|
|||||||
secure := true
|
secure := true
|
||||||
httpOnly := true
|
httpOnly := true
|
||||||
|
|
||||||
if !constants.IS_PROD {
|
|
||||||
secure = false
|
|
||||||
}
|
|
||||||
|
|
||||||
host := GetHostName(constants.AUTHORIZER_URL)
|
host := GetHostName(constants.AUTHORIZER_URL)
|
||||||
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
gc.SetSameSite(http.SameSiteNoneMode)
|
||||||
gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
|
gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
|
||||||
}
|
}
|
||||||
|
@@ -74,7 +74,7 @@ func SendVerificationMail(toEmail, token string) error {
|
|||||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
||||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
||||||
<p>Hey there 👋</p>
|
<p>Hey there 👋</p>
|
||||||
<p>We received a request to sign-up for <b>%s</b>. If this is correct, please confirm your email address by clicking the button below.</p> <br/>
|
<p>We received a request to sign-up / login for <b>%s</b>. If this is correct, please confirm your email address by clicking the button below.</p> <br/>
|
||||||
<a href="%s" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Confirm Email</a>
|
<a href="%s" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Confirm Email</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@@ -16,5 +16,6 @@ func GetMetaInfo() model.Meta {
|
|||||||
IsTwitterLoginEnabled: constants.TWITTER_CLIENT_ID != "" && constants.TWITTER_CLIENT_SECRET != "",
|
IsTwitterLoginEnabled: constants.TWITTER_CLIENT_ID != "" && constants.TWITTER_CLIENT_SECRET != "",
|
||||||
IsBasicAuthenticationEnabled: constants.DISABLE_BASIC_AUTHENTICATION != "true",
|
IsBasicAuthenticationEnabled: constants.DISABLE_BASIC_AUTHENTICATION != "true",
|
||||||
IsEmailVerificationEnabled: constants.DISABLE_EMAIL_VERIFICATION != "true",
|
IsEmailVerificationEnabled: constants.DISABLE_EMAIL_VERIFICATION != "true",
|
||||||
|
IsMagicLoginEnabled: constants.DISABLE_MAGIC_LOGIN != "true" && constants.DISABLE_EMAIL_VERIFICATION != "true",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
server/utils/requestInfo.go
Normal file
19
server/utils/requestInfo.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func GetIP(r *http.Request) string {
|
||||||
|
IPAddress := r.Header.Get("X-Real-Ip")
|
||||||
|
if IPAddress == "" {
|
||||||
|
IPAddress = r.Header.Get("X-Forwarded-For")
|
||||||
|
}
|
||||||
|
|
||||||
|
if IPAddress == "" {
|
||||||
|
IPAddress = r.RemoteAddr
|
||||||
|
}
|
||||||
|
return IPAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUserAgent(r *http.Request) string {
|
||||||
|
return r.UserAgent()
|
||||||
|
}
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// function to get hostname
|
// GetHostName function to get hostname
|
||||||
func GetHostName(auth_url string) string {
|
func GetHostName(auth_url string) string {
|
||||||
u, err := url.Parse(auth_url)
|
u, err := url.Parse(auth_url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
|
RedirectURL string `json:"redirect_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CustomClaim struct {
|
type CustomClaim struct {
|
||||||
@@ -28,7 +29,7 @@ func CreateVerificationToken(email string, tokenType string) (string, error) {
|
|||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
},
|
},
|
||||||
tokenType,
|
tokenType,
|
||||||
UserInfo{Email: email, Host: constants.AUTHORIZER_URL},
|
UserInfo{Email: email, Host: constants.AUTHORIZER_URL, RedirectURL: constants.APP_URL},
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.SignedString([]byte(constants.JWT_SECRET))
|
return t.SignedString([]byte(constants.JWT_SECRET))
|
||||||
|
Reference in New Issue
Block a user