Okta Integration - Bring Okta OAuth as an available option (#128)
- Edited package.json for react bug + https://github.com/ReactTraining/react-router/issues/6630
This commit is contained in:
parent
ea5f8c888e
commit
63d502fce1
|
@ -15,7 +15,7 @@
|
|||
- Expirable Links
|
||||
- URL deletion
|
||||
- Multiple authorization strategies:
|
||||
- Local authorization via OAuth 2.0 (Google, GitHub and Microsoft)
|
||||
- Local authorization via OAuth 2.0 (Google, GitHub, Microsoft, and Okta)
|
||||
- Proxy authorization for running behind e.g. [Google IAP](https://cloud.google.com/iap/)
|
||||
- Easy [ShareX](https://github.com/ShareX/ShareX) integration
|
||||
- Dockerizable
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
ListenAddr: ':8080' # Consists of 'IP:Port', e.g. ':8080' listens on any IP and on Port 8080
|
||||
BaseURL: 'http://localhost:3000' # Origin URL, required for the authentication via OAuth callback
|
||||
BaseURL: 'http://localhost:8080' # Origin URL, required for the authentication via OAuth callback
|
||||
DisplayURL: '' # (OPTIONAL) Display URL, how the apication will present itself in the UI - if not set, defaults to BaseURL
|
||||
Backend: boltdb # Can be 'boltdb' or 'redis'
|
||||
DataDir: ./data # Contains: the database and the private key
|
||||
|
@ -10,14 +10,18 @@ ShortedIDLength: 10 # Length of the random generated ID which is used for new sh
|
|||
AuthBackend: oauth # Can be 'oauth' or 'proxy'
|
||||
Google: # only relevant when using the oauth authbackend
|
||||
ClientID: replace me
|
||||
ClientSecret: replace me
|
||||
ClientSecret: 'replace me'
|
||||
GitHub: # only relevant when using the oauth authbackend
|
||||
ClientID: replace me
|
||||
ClientSecret: replace me
|
||||
ClientSecret: 'replace me'
|
||||
EndpointURL: # (OPTIONAL) URL for custom endpoint (currently only for github); e.g. 'https://github.mydomain.com'
|
||||
Microsoft: # only relevant when using the oauth authbackend
|
||||
ClientID: replace me
|
||||
ClientSecret: 'replace me'
|
||||
Okta: # only relevant when using the oauth authbackend
|
||||
ClientID: replace me
|
||||
ClientSecret: 'replace me'
|
||||
EndpointURL: # (MANDATORY) Issuer URL from the OAuth API => Authorization Servers in Okta
|
||||
Proxy: # only relevant when using the proxy authbackend
|
||||
RequireUserHeader: false # If true, will reject connections that do not have the UserHeader set
|
||||
UserHeader: "X-Goog-Authenticated-User-ID" # pull the unique user ID from this header
|
||||
|
|
|
@ -41,6 +41,11 @@ func (h *Handler) initOAuth() {
|
|||
auth.WithAdapterWrapper(auth.NewMicrosoftAdapter(microsoft.ClientID, microsoft.ClientSecret), h.engine.Group("/api/v1/auth/microsoft"))
|
||||
h.providers = append(h.providers, "microsoft")
|
||||
}
|
||||
okta := util.GetConfig().Okta
|
||||
if okta.Enabled() {
|
||||
auth.WithAdapterWrapper(auth.NewOktaAdapter(okta.ClientID, okta.ClientSecret, okta.EndpointURL), h.engine.Group("/api/v1/auth/okta"))
|
||||
h.providers = append(h.providers, "okta")
|
||||
}
|
||||
|
||||
h.engine.POST("/api/v1/auth/check", h.handleAuthCheck)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/mxschmitt/golang-url-shortener/internal/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type oktaAdapter struct {
|
||||
config *oauth2.Config
|
||||
}
|
||||
|
||||
// NewOktaAdapter creates an oAuth adapter out of the credentials and the baseURL
|
||||
func NewOktaAdapter(clientID, clientSecret, endpointURL string) Adapter {
|
||||
|
||||
if endpointURL == "" {
|
||||
logrus.Error("Configure Okta Endpoint")
|
||||
}
|
||||
|
||||
return &oktaAdapter{&oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: util.GetConfig().BaseURL + "/api/v1/auth/okta/callback",
|
||||
Scopes: []string{
|
||||
"profile",
|
||||
"openid",
|
||||
"offline_access",
|
||||
},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: endpointURL + "/v1/authorize",
|
||||
TokenURL: endpointURL + "/v1/token",
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
func (a *oktaAdapter) GetRedirectURL(state string) string {
|
||||
return a.config.AuthCodeURL(state)
|
||||
}
|
||||
|
||||
func (a *oktaAdapter) GetUserData(state, code string) (*user, error) {
|
||||
|
||||
logrus.Debugf("Getting User Data with state: %s, and code: %s", state, code)
|
||||
oAuthToken, err := a.config.Exchange(context.Background(), code)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not exchange code")
|
||||
}
|
||||
if util.GetConfig().Okta.EndpointURL == "" {
|
||||
logrus.Error("Okta EndpointURL is Empty")
|
||||
}
|
||||
oktaUrl, err := url.Parse(util.GetConfig().Okta.EndpointURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not parse Okta EndpointURL")
|
||||
}
|
||||
oktaBaseURL := strings.Replace(oktaUrl.String(), oktaUrl.RequestURI(), "", 1)
|
||||
oAuthUserInfoReq, err := a.config.Client(context.Background(), oAuthToken).Get(oktaBaseURL + "/api/v1/users/me")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get user data")
|
||||
}
|
||||
defer oAuthUserInfoReq.Body.Close()
|
||||
var oUser struct {
|
||||
ID int `json:"sub"`
|
||||
// Custom URL property for user Avatar can go here
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err = json.NewDecoder(oAuthUserInfoReq.Body).Decode(&oUser); err != nil {
|
||||
return nil, errors.Wrap(err, "decoding user info failed")
|
||||
}
|
||||
return &user{
|
||||
ID: string(oUser.ID),
|
||||
Name: oUser.Name,
|
||||
Picture: util.GetConfig().BaseURL + "/images/okta_logo.png", // Default Okta Avatar
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *oktaAdapter) GetOAuthProviderName() string {
|
||||
return "okta"
|
||||
}
|
|
@ -28,6 +28,7 @@ type Configuration struct {
|
|||
Google oAuthConf `yaml:"Google" env:"GOOGLE"`
|
||||
GitHub oAuthConf `yaml:"GitHub" env:"GITHUB"`
|
||||
Microsoft oAuthConf `yaml:"Microsoft" env:"MICROSOFT"`
|
||||
Okta oAuthConf `yaml:"Okta" env:"OKTA"`
|
||||
Proxy proxyAuthConf `yaml:"Proxy" env:"PROXY"`
|
||||
Redis redisConf `yaml:"Redis" env:"REDIS"`
|
||||
}
|
||||
|
@ -46,7 +47,7 @@ type redisConf struct {
|
|||
type oAuthConf struct {
|
||||
ClientID string `yaml:"ClientID" env:"CLIENT_ID"`
|
||||
ClientSecret string `yaml:"ClientSecret" env:"CLIENT_SECRET"`
|
||||
EndpointURL string `yaml:"EndPointURL" env:"ENDPOINT_URL"` // optional for only GitHub
|
||||
EndpointURL string `yaml:"EndpointURL" env:"ENDPOINT_URL"` // Optional for GitHub, mandatory for Okta
|
||||
}
|
||||
|
||||
type proxyAuthConf struct {
|
||||
|
@ -58,7 +59,7 @@ type proxyAuthConf struct {
|
|||
// Config contains the default values
|
||||
var Config = Configuration{
|
||||
ListenAddr: ":8080",
|
||||
BaseURL: "http://localhost:3000",
|
||||
BaseURL: "http://localhost:8080",
|
||||
DisplayURL: "",
|
||||
DataDir: "data",
|
||||
Backend: "boltdb",
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"resolutions": {
|
||||
"react-router": "4.3.1"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -131,6 +131,12 @@ export default class BaseComponent extends Component {
|
|||
<Button style={{ backgroundColor: "#333", color: "white" }} onClick={this.onOAuthClick.bind(this, "github")}>
|
||||
<Icon name='github' /> Login with GitHub
|
||||
</Button>
|
||||
{info.providers.includes("okta") && <div className="ui divider"></div>}
|
||||
</div>}
|
||||
{info.providers.includes("okta") && <div>
|
||||
<Button style={{ color: "#007dc1" }} onClick={this.onOAuthClick.bind(this, "okta")}>
|
||||
<Image src='/images/okta_logo.png' style={{ width: "16px", height: "16px", marginBottom: ".15em" }} avatar /> Login with Okta
|
||||
</Button>
|
||||
</div>}
|
||||
{info.providers.includes("microsoft") && <div>
|
||||
<div className="ui divider"></div>
|
||||
|
|
Loading…
Reference in New Issue