Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support additional authentication methods - Oauth #29

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0ecc411
testing
CubicrootXYZ May 4, 2021
de3f94f
remove testing
CubicrootXYZ May 4, 2021
2c4400e
add PoC for oauth2
CubicrootXYZ May 9, 2021
b79b946
add password anmd user check
CubicrootXYZ May 9, 2021
a247db1
started moving oauth to current authentication
CubicrootXYZ May 11, 2021
7c5ce31
integrate oauth in current auth
CubicrootXYZ May 13, 2021
609eb66
move things again and introduce jwt tokens
CubicrootXYZ May 17, 2021
6ee285d
move to authhandler
CubicrootXYZ May 22, 2021
2dce377
get /auth to work properly
CubicrootXYZ May 23, 2021
e78ffda
stack authentication layers & add documentation
CubicrootXYZ May 23, 2021
f60bb83
add refresh config
CubicrootXYZ May 23, 2021
46027e2
clean up & add file support
CubicrootXYZ May 23, 2021
fc6bc82
move token key to config & clean up
CubicrootXYZ May 30, 2021
393d586
removed replaced code
CubicrootXYZ May 30, 2021
69970e4
Merge branch 'pushbits:master' into oauth-testing
CubicrootXYZ May 30, 2021
cee8001
clean up errors
CubicrootXYZ Jun 2, 2021
0504528
wording & consistency
CubicrootXYZ Jun 3, 2021
364e522
panic when no oauth secrets are set
CubicrootXYZ Jun 3, 2021
a06fe24
clean up & consistency
CubicrootXYZ Jun 3, 2021
1e2114b
register auth handler
CubicrootXYZ Jun 3, 2021
43b2908
move to POST
CubicrootXYZ Jun 3, 2021
48b4f71
added longterm tokens
CubicrootXYZ Jun 5, 2021
5b0c124
use body data for auth
CubicrootXYZ Jun 5, 2021
488f426
reflect changes in readme
CubicrootXYZ Jun 5, 2021
c92b783
let oauth use existing db
CubicrootXYZ Jun 19, 2021
1a1ee00
Merge branch 'master' into oauth-testing
CubicrootXYZ Jul 21, 2021
a5e6472
clean up merge conflicts
CubicrootXYZ Jul 21, 2021
b226a5e
add missing package
CubicrootXYZ Jul 21, 2021
d98aa1c
lowercase errors
CubicrootXYZ Jul 21, 2021
bed622e
allow multiple oauth clients
CubicrootXYZ Sep 12, 2021
4fe8d7f
Merge branch 'master' into oauth-testing
CubicrootXYZ Nov 21, 2021
2c62a49
add ginserver again
CubicrootXYZ Nov 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,125 @@ You can retrieve the token using [pbcli](https://github.com/PushBits/cli) by run
pbcli application show $PB_APPLICATION --url https://pushbits.example.com --username $PB_USERNAME
```

### Authentication

Pushbits offers you two methods of authenticating against the server:

* Basic authentication (`basic`)
* Oauth 2 (`oauth`)
eikendev marked this conversation as resolved.
Show resolved Hide resolved

You will find the corresponding setting in the security section.

```yaml
...
security:
...
# The authentication method to use
authentication: basic
...
```

#### Basic authentication

For [basic authentication](https://de.wikipedia.org/wiki/HTTP-Authentifizierung) you have to provide your username and password in each request to the server. For example in curl you can do this with the `--user` flag:
eikendev marked this conversation as resolved.
Show resolved Hide resolved

```bash
curl -u myusername:totalysecretpassword
eikendev marked this conversation as resolved.
Show resolved Hide resolved
```

#### Oauth 2
eikendev marked this conversation as resolved.
Show resolved Hide resolved

[Oauth 2](https://de.wikipedia.org/wiki/OAuth) is a token based authentication method. Instead of passing your password with each request you request a token from an authorization server. With this token you are then able to authenticate yourself against the pushbits server.
eikendev marked this conversation as resolved.
Show resolved Hide resolved

Make sure to setup the "oauth" section in the config file correctly.

##### Authenticating

For authentication use the ``/oauth2/auth` endpoint. E.g.:

```bash
curl \
--header "Content-Type: application/json" \
--request GET \
"https://pushbits.example.com/oauth2/auth?client_id=000000&username=admin&password=1233456&response_type=code&redirect_uri=https://myapp.example.com"
```

This will return a HTTP redirect with the status code `302` and an authentication code set as parameter:

```
HTTP/2 302
date: Sun, 23 May 2021 10:33:27 GMT
location: https://myapp.example.com?code=4T1TJXMBPTOS4NNGILBDYW
content-length: 0
```

Your app then needs to use this code to trade it for a access token.

**Hint for command line users:** you can extract the authentication code from the redirect without the need of a running webserver.

##### Receiving an access token

You can get an access token from the `/oauth/token` endpoint. There are several methods, so called "grant types" for receiving a token. Pushbits currently supports the following one's:

* Refresh
* Authentication code

Oauth2 authentication is based on "clients", thus you need to provide identifieres for a client with your request. These are the `client_id` and the `client_secret`.
eikendev marked this conversation as resolved.
Show resolved Hide resolved

For your first token you will need a authentication code, see the section above. Then use it like this:

```bash
curl \
--header "Content-Type: application/json" \
--request GET \
"https://pushbits.example.com/oauth2/token?grant_type=authorization_code&client_id=000000&client_secret=49gjg4js9&response_type=token&redirect_uri=https://myapp.example.com&code=OP1Q2UJEVL-RPR9GZAUURA"
eikendev marked this conversation as resolved.
Show resolved Hide resolved
```

This will then return an access token and refresh token for you.

```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIwMDAwMDAiLCJleHAiOjE2MjE4NTU3ODcsInN1YiI6IjEifQ.jMux7CBw6fY15Ohc8exEbcnUiMBVVgCowvq3rMrw7MQ",
"expires_in": 86400,
"refresh_token": "OP1Q2UJEVL-RPR9GZAUURA",
"token_type": "Bearer"
}
```

The access token is short lived, the refresh token is long lived, but can not be used for authentication. If your access token runs out, you can use the refresh token to generate a new access token:

```bash
curl \
--header "Content-Type: application/json" \
--request GET \
"https://pushbits.example.com/oauth2/token?grant_type=refresh_token&client_id=000000&client_secret=49gjg4js9&response_type=token&refresh_token=OP1Q2UJEVL-RPR9GZAUURA"
```

##### Getting information about a access token

With a valid access token you can get information about it from `/oauth/tokeninfo`. This is ment for testing if a token is issued correctly.
eikendev marked this conversation as resolved.
Show resolved Hide resolved

```bash
curl \
--header "Content-Type: application/json" \
--request GET \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIwMDAwMDAiLCJleHAiOjE2MjE4NTU3ODcsInN1YiI6IjEifQ.jMux7CBw6fY15Ohc8exEbcnUiMBVVgCowvq3rMrw7MQ" \
"https://pushbits.example.com/oauth2/tokeninfo"
```

##### Revoking a token

Admin users are eligable to revoke tokens. This should not be necessary in normal operation, as tokens are only short lived. But there might be situations where attackers might have gotten knowledge about a token.

```bash
curl \
--header "Content-Type: application/json" \
--request POST \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIwMDAwMDAiLCJleHAiOjE2MjE4NTU3ODcsInN1YiI6IjEifQ.jMux7CBw6fY15Ohc8exEbcnUiMBVVgCowvq3rMrw7MQ" \
--data '{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIwMDAwMDAiLCJleHAiOjE2MjE4NDg1MDYsInN1YiI6IjEifQ.cO0_8fqsJDG4KswjC0CSzc_EznntH-FDQejdolPAISo"}'
"https://pushbits.example.com/oauth2/revoke"
```

### Message options

Messages are supporting three different syntaxes:
Expand Down
2 changes: 1 addition & 1 deletion cmd/pushbits/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func main() {
log.Fatal(err)
}

engine := router.Create(c.Debug, cm, db, dp)
engine := router.Create(c.Debug, cm, db, dp, c.Authentication)

runner.Run(engine, c.HTTP.ListenAddress, c.HTTP.Port)
}
19 changes: 19 additions & 0 deletions config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,22 @@ crypto:
formatting:
# Whether to use colored titles based on the message priority (<0: grey, 0-3: default, 4-10: yellow, 10-20: orange, >20: red).
coloredtitle: false

authentication:
# The authentication method to use.
method: basic
# Only needed if you choose oauth method.
oauth:
# The storage used for tokens.
storage: "file"
# The connection to the storage. For file: the filename, ending with .db. For mysql: the connection string. Check out
# https://github.com/go-sql-driver/mysql#dsn-data-source-name for details.
connection: "pushbits_token.db"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to have a separate file for the tokens? sqlite is already an option for the user data, guess we can have a new table in the same file?

# Oauth client identifier (random string).
clientid: "000000"
# Oauth client secret.
clientsecret: "123456"
eikendev marked this conversation as resolved.
Show resolved Hide resolved
# Oauth redirect url after successful auth. Can be overwritten in the authentication request.
clientredirect: "http://localhost"
# Key used for signing the access tokens (JWT with HS512)
tokenkey: "123456"
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ go 1.14

require (
github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/gin-contrib/location v0.0.2
github.com/gin-gonic/gin v1.6.3
github.com/go-oauth2/gin-server v1.0.0
github.com/go-playground/validator/v10 v10.3.0 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/gomarkdown/markdown v0.0.0-20210408062403-ad838ccf8cdd
github.com/google/go-cmp v0.5.0 // indirect
github.com/imrenagi/go-oauth2-mysql v1.1.0
github.com/jinzhu/configor v1.2.1
github.com/jmoiron/sqlx v1.3.3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is related to the separate sqlite file: What's the reason for switching to sqlx when we already have GORM?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now that it is integrated with oauth. Maybe we can investigate this further. Having two different databases increases complexity.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not very clean, but it is possible to use the same mysql database for auth and pushbits itself by just using the same credentials.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I managed to improve that one step further - oauth now does not need any further storage information. If the main db is mysql, oauth uses just the same database and if it is sqlite3 it uses its own sqlite file.

Unfortunately I was not yet able to share the main sqlite with oauth. For that I'd need a sqlx object with the sqlite - which I am able to generate but it then runs in "command unknown" errors as soon as actions are performed on it.

github.com/json-iterator/go v1.1.10 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd
Expand All @@ -19,6 +23,7 @@ require (
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/ugorji/go v1.2.4 // indirect
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
gopkg.in/oauth2.v3 v3.12.0
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.0.4
gorm.io/driver/sqlite v1.1.4
Expand Down
Loading