Cleanup of callback script and service logs.
Updated readme with setup instructions
This commit is contained in:
parent
ccb516f8ee
commit
d22da69e2a
68
README.md
Normal file
68
README.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# SCM OAuth Provider
|
||||||
|
|
||||||
|
This is a lightweight Go server for handling OAuth flows with Gitea, Gitlab, Bitbucket, GitHub.
|
||||||
|
|
||||||
|
> Note: My primary use case is providing OAuth between Gitea and NetlifyCMS. Other SCMs are untested as of now.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Open the repo and build the service:
|
||||||
|
|
||||||
|
```
|
||||||
|
go build -o oauth-provider .
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy the binary to your server.
|
||||||
|
|
||||||
|
> Dockerfile is coming soon
|
||||||
|
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
The service needs some minimal configuration set before it can run.
|
||||||
|
On the server or the location you are running the service, create a config file:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir ./env
|
||||||
|
touch ./env/config
|
||||||
|
```
|
||||||
|
|
||||||
|
The config file is TOML based. You can see a complete example in this repo at `./env/sample.config`
|
||||||
|
|
||||||
|
```
|
||||||
|
[runtime]
|
||||||
|
# Not used anywhere yet, for information only
|
||||||
|
environment="development"
|
||||||
|
|
||||||
|
[server]
|
||||||
|
# The hostname to serve from; Your external app will connect to the OAuth provider via this URL
|
||||||
|
host="localhost"
|
||||||
|
# The port to serve from; Used in conjunction with [server.host] to create a complete URL
|
||||||
|
port="3000"
|
||||||
|
# Used with OAuth provider sessions
|
||||||
|
sessionSecret="super-secret"
|
||||||
|
```
|
||||||
|
|
||||||
|
For each CMS, there are some required settings:
|
||||||
|
|
||||||
|
```
|
||||||
|
[gitea]
|
||||||
|
# OAuth Key and Secret generated on the SCM site
|
||||||
|
key="<KEY>"
|
||||||
|
secret="<SECRET>"
|
||||||
|
# URL of the SCM instance
|
||||||
|
baseUrl="https://gitea.company.com"
|
||||||
|
# URI of the authorize endpoint (e.g for Gitea, this is shown when creating the OAuth application)
|
||||||
|
authUri="login/oauth/authorize"
|
||||||
|
# URI of the access_token endpoint (e.g for Gitea, this is shown when creating the OAuth application)
|
||||||
|
accessTokenUri="login/oauth/access_token"
|
||||||
|
# URI of the authorize endpoint if overridden (e.g for Gitea, this is shown when creating the OAuth application)
|
||||||
|
userUri="api/v1/user"
|
||||||
|
# Callback URL for the SCM, where it will redirect the user after they authorise. This needs to match what was given when creating the OAuth application.
|
||||||
|
callbackUri="http://localhost:3000/callback/gitea"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Credits
|
||||||
|
|
||||||
|
Inspiration taken from https://github.com/igk1972/netlify-cms-oauth-provider-go
|
106
main.go
106
main.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net"
|
"net"
|
||||||
@ -21,8 +22,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
serviceName = "netlify-cms-oauth-provider"
|
serviceName = "oauth-provider"
|
||||||
msgTemplate = `<!DOCTYPE html><html><head></head><body>{{.}}</body></html>`
|
msgTemplate = `<!DOCTYPE html><html><head></head><body>{{.}}</body></html>`
|
||||||
|
resTemplate = `
|
||||||
|
<!DOCTYPE html><html><head></head><body>
|
||||||
|
<script>
|
||||||
|
function recieveMsg(e) {
|
||||||
|
window.opener.postMessage("{{.OAuthResult}}", e.origin);
|
||||||
|
}
|
||||||
|
window.addEventListener("message", recieveMsg, false);
|
||||||
|
window.opener.postMessage("{{.Provider}}", "*");
|
||||||
|
</script>
|
||||||
|
</body></html>`
|
||||||
|
|
||||||
log *logrus.Entry
|
log *logrus.Entry
|
||||||
config *viper.Viper
|
config *viper.Viper
|
||||||
@ -45,7 +56,7 @@ func initProviders() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
type settings struct {
|
type settings struct {
|
||||||
key, secret, baseURL, callbackURL, authURI, accessTokenURI, userURI string
|
key, secret, BaseURL, CallbackURL, AuthURI, AccessTokenURI, UserURI string
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("initialising providers")
|
log.Info("initialising providers")
|
||||||
@ -55,11 +66,11 @@ func initProviders() {
|
|||||||
return settings{
|
return settings{
|
||||||
key: config.GetString(name + ".key"),
|
key: config.GetString(name + ".key"),
|
||||||
secret: config.GetString(name + ".secret"),
|
secret: config.GetString(name + ".secret"),
|
||||||
baseURL: baseURL,
|
BaseURL: baseURL,
|
||||||
authURI: fmt.Sprintf("%s/%s", baseURL, config.GetString(name+".authURI")),
|
AuthURI: fmt.Sprintf("%s/%s", baseURL, config.GetString(name+".authURI")),
|
||||||
accessTokenURI: fmt.Sprintf("%s/%s", baseURL, config.GetString(name+".accessTokenURI")),
|
AccessTokenURI: fmt.Sprintf("%s/%s", baseURL, config.GetString(name+".accessTokenURI")),
|
||||||
userURI: fmt.Sprintf("%s/%s", baseURL, config.GetString(name+".userURI")),
|
UserURI: fmt.Sprintf("%s/%s", baseURL, config.GetString(name+".userURI")),
|
||||||
callbackURL: config.GetString(name + ".callbackURI"),
|
CallbackURL: config.GetString(name + ".callbackURI"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,11 +78,12 @@ func initProviders() {
|
|||||||
log.Info("- adding gitea provider")
|
log.Info("- adding gitea provider")
|
||||||
var p goth.Provider
|
var p goth.Provider
|
||||||
s := getProviderSetings("gitea")
|
s := getProviderSetings("gitea")
|
||||||
if s.authURI != "" {
|
if s.AuthURI != "" {
|
||||||
log.Infof("-- with custom settings %+v", s)
|
out, _ := json.MarshalIndent(s, "", " ")
|
||||||
p = gitea.NewCustomisedURL(s.key, s.secret, s.callbackURL, s.authURI, s.accessTokenURI, s.userURI)
|
log.Infof("-- with custom settings %s", string(out))
|
||||||
|
p = gitea.NewCustomisedURL(s.key, s.secret, s.CallbackURL, s.AuthURI, s.AccessTokenURI, s.UserURI)
|
||||||
} else {
|
} else {
|
||||||
p = gitea.New(s.key, s.secret, s.callbackURL)
|
p = gitea.New(s.key, s.secret, s.CallbackURL)
|
||||||
}
|
}
|
||||||
providers = append(providers, p)
|
providers = append(providers, p)
|
||||||
}
|
}
|
||||||
@ -80,11 +92,12 @@ func initProviders() {
|
|||||||
log.Info("- adding gitlab provider")
|
log.Info("- adding gitlab provider")
|
||||||
var p goth.Provider
|
var p goth.Provider
|
||||||
s := getProviderSetings("gitlab")
|
s := getProviderSetings("gitlab")
|
||||||
if s.authURI != "" {
|
if s.AuthURI != "" {
|
||||||
log.Infof("-- with custom settings %+v", s)
|
out, _ := json.MarshalIndent(s, "", " ")
|
||||||
p = gitlab.NewCustomisedURL(s.key, s.secret, s.callbackURL, s.authURI, s.accessTokenURI, s.userURI)
|
log.Infof("-- with custom settings %s", string(out))
|
||||||
|
p = gitlab.NewCustomisedURL(s.key, s.secret, s.CallbackURL, s.AuthURI, s.AccessTokenURI, s.UserURI)
|
||||||
} else {
|
} else {
|
||||||
p = gitlab.New(s.key, s.secret, s.callbackURL)
|
p = gitlab.New(s.key, s.secret, s.CallbackURL)
|
||||||
}
|
}
|
||||||
providers = append(providers, p)
|
providers = append(providers, p)
|
||||||
}
|
}
|
||||||
@ -93,7 +106,7 @@ func initProviders() {
|
|||||||
log.Info("- adding github provider")
|
log.Info("- adding github provider")
|
||||||
var p goth.Provider
|
var p goth.Provider
|
||||||
s := getProviderSetings("github")
|
s := getProviderSetings("github")
|
||||||
p = github.New(s.key, s.secret, s.callbackURL)
|
p = github.New(s.key, s.secret, s.CallbackURL)
|
||||||
providers = append(providers, p)
|
providers = append(providers, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +114,7 @@ func initProviders() {
|
|||||||
log.Info("- adding bitbucket provider")
|
log.Info("- adding bitbucket provider")
|
||||||
var p goth.Provider
|
var p goth.Provider
|
||||||
s := getProviderSetings("bitbucket")
|
s := getProviderSetings("bitbucket")
|
||||||
p = bitbucket.New(s.key, s.secret, s.callbackURL)
|
p = bitbucket.New(s.key, s.secret, s.CallbackURL)
|
||||||
providers = append(providers, p)
|
providers = append(providers, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,37 +122,6 @@ func initProviders() {
|
|||||||
goth.UseProviders(providers...)
|
goth.UseProviders(providers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
script = `<!DOCTYPE html><html><head><script>
|
|
||||||
if (!window.opener) {
|
|
||||||
window.opener = {
|
|
||||||
postMessage: function(action, origin) {
|
|
||||||
console.log(action, origin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(function(status, provider, result) {
|
|
||||||
function recieveMessage(e) {
|
|
||||||
console.log("Recieve message:", e);
|
|
||||||
// send message to main window with da app
|
|
||||||
console.log("Sending message:", "authorization:" + provider + ":" + status + ":" + result, e.origin)
|
|
||||||
window.opener.postMessage(
|
|
||||||
"authorization:" + provider + ":" + status + ":" + result,
|
|
||||||
e.origin
|
|
||||||
);
|
|
||||||
}
|
|
||||||
window.addEventListener("message", recieveMessage, false);
|
|
||||||
|
|
||||||
// Start handshare with parent
|
|
||||||
console.log("Sending message:", "authorizing:" + provider, "*")
|
|
||||||
window.opener.postMessage(
|
|
||||||
"authorizing:" + provider,
|
|
||||||
"*"
|
|
||||||
);
|
|
||||||
})(%#v, %#v, %#v)
|
|
||||||
</script></head><body></body></html>`
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log = logrus.New().WithFields(logrus.Fields{
|
log = logrus.New().WithFields(logrus.Fields{
|
||||||
"service": serviceName,
|
"service": serviceName,
|
||||||
@ -157,6 +139,8 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
r.HandleFunc("/callback/{provider}", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/callback/{provider}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
provider, err := gothic.GetProviderName(r)
|
provider, err := gothic.GetProviderName(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("callback: GetProviderName failed %v", err)
|
log.Errorf("callback: GetProviderName failed %v", err)
|
||||||
@ -168,16 +152,18 @@ func main() {
|
|||||||
log.Errorf("callback: CompleteUserAuth failed %v", err)
|
log.Errorf("callback: CompleteUserAuth failed %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Info("logged in user")
|
|
||||||
// t, _ := template.New("msg").Parse(msgTemplate)
|
|
||||||
// t.Execute(w, fmt.Sprintf("Connected with UserID '%s'", user.UserID))
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
log.Infof("logged in user to '%s'\n", vars["provider"])
|
||||||
w.WriteHeader(http.StatusOK)
|
t, _ := template.New("res").Parse(resTemplate)
|
||||||
result := fmt.Sprintf(`{"token":"%s", "provider":"%s"}`, user.AccessToken, user.Provider)
|
|
||||||
|
|
||||||
log.Info("details: %+v", user)
|
data := struct {
|
||||||
w.Write([]byte(fmt.Sprintf(script, "success", provider, result)))
|
Provider string
|
||||||
|
OAuthResult string
|
||||||
|
}{
|
||||||
|
Provider: fmt.Sprintf(`authorizing:%s`, provider),
|
||||||
|
OAuthResult: fmt.Sprintf(`authorization:%s:%s:{"token":"%s", "provider":"%s"}`, provider, "success", user.AccessToken, user.Provider),
|
||||||
|
}
|
||||||
|
t.Execute(w, data)
|
||||||
}).Methods("GET")
|
}).Methods("GET")
|
||||||
|
|
||||||
// redirect to correct auth/{provider} URL if Auth request is submited with a query param '&provider=X'
|
// redirect to correct auth/{provider} URL if Auth request is submited with a query param '&provider=X'
|
||||||
@ -191,7 +177,9 @@ func main() {
|
|||||||
}).Methods("GET")
|
}).Methods("GET")
|
||||||
|
|
||||||
r.HandleFunc("/auth/{provider}", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/auth/{provider}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Infof("handling auth provider request '%s'\n", r)
|
vars := mux.Vars(r)
|
||||||
|
log.Infof("handling auth provider request '%s'\n", vars["provider"])
|
||||||
|
|
||||||
if gothUser, err := gothic.CompleteUserAuth(w, r); err == nil {
|
if gothUser, err := gothic.CompleteUserAuth(w, r); err == nil {
|
||||||
t, _ := template.New("msg").Parse(msgTemplate)
|
t, _ := template.New("msg").Parse(msgTemplate)
|
||||||
t.Execute(w, fmt.Sprintf("Connected to existing session with UserID '%s'", gothUser.UserID))
|
t.Execute(w, fmt.Sprintf("Connected to existing session with UserID '%s'", gothUser.UserID))
|
||||||
@ -201,7 +189,9 @@ func main() {
|
|||||||
}).Methods("GET")
|
}).Methods("GET")
|
||||||
|
|
||||||
r.HandleFunc("/logout/{provider}", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/logout/{provider}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Infof("logout with '%s'\n", r)
|
vars := mux.Vars(r)
|
||||||
|
log.Infof("logout from '%s'\n", vars["provider"])
|
||||||
|
|
||||||
gothic.Logout(w, r)
|
gothic.Logout(w, r)
|
||||||
w.Header().Set("Location", "/")
|
w.Header().Set("Location", "/")
|
||||||
w.WriteHeader(http.StatusTemporaryRedirect)
|
w.WriteHeader(http.StatusTemporaryRedirect)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user