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

Oauth 4 okta #112

Merged
merged 9 commits into from
Mar 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/vendor
.okta
49 changes: 47 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@ client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), o

Hard-coding the Okta domain and API token works for quick tests, but for real projects you should use a more secure way of storing these values (such as environment variables). This library supports a few different configuration sources, covered in the [configuration reference](#configuration-reference) section.

### OAuth 2.0

Okta allows you to interact with Okta APIs using scoped OAuth 2.0 access tokens. Each access token enables the bearer to perform specific actions on specific Okta endpoints, with that ability controlled by which scopes the access token contains.

This SDK supports this feature only for service-to-service applications. Check out [our guides](https://developer.okta.com/docs/guides/implement-oauth-for-okta/overview/) to learn more about how to register a new service application using a private and public key pair.

When using this approach you won't need an API Token because the SDK will request an access token for you. In order to use OAuth 2.0, construct a client instance by passing the following parameters:

```
client, _ := okta.NewClient(context,
okta.WithAuthorizationMode("PrivateKey"),
okta.WithClientId("{{clientId}}),
okta.WithScopes(([]string{"okta.users.manage"})),
okta.WithPrivateKey({{PEM PRIVATE KEY BLOCK}})
)
```

### Extending the Client
When calling `okta.NewClient()` we allow for you to pass custom instances of `http.Client` and `cache.Cache`.

Expand Down Expand Up @@ -354,15 +371,15 @@ return authServer, resp, nil
This library looks for configuration in the following sources:

0. An `okta.yaml` file in a `.okta` folder in the current user's home directory (`~/.okta/okta.yaml` or `%userprofile\.okta\okta.yaml`)
0. An `okta.yaml` file in a `.okta` folder in the application or project's root directory
0. A `.okta.yaml` file in the application or project's root directory
0. Environment variables
0. Configuration explicitly passed to the constructor (see the example in [Getting started](#getting-started))

Higher numbers win. In other words, configuration passed via the constructor will override configuration found in environment variables, which will override configuration in `okta.yaml` (if any), and so on.

### YAML configuration

The full YAML configuration looks like:
When you use an API Token instead of OAuth 2.0 the full YAML configuration looks like:

```yaml
okta:
Expand All @@ -377,6 +394,34 @@ okta:
token: {apiToken}
```

When you use OAuth 2.0 the full YAML configuration looks like:

```yaml
okta:
client:
connectionTimeout: 30 # seconds
orgUrl: "https://{yourOktaDomain}"
proxy:
port: null
host: null
username: null
password: null
authorizationMode: "PrivateKey"
clientId: "{yourClientId}"
scopes:
- scope.1
- scope.2
privateKey: |
bretterer marked this conversation as resolved.
Show resolved Hide resolved
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAl4F5CrP6Wu2kKwH1Z+CNBdo0iteHhVRIXeHdeoqIB1iXvuv4
THQdM5PIlot6XmeV1KUKuzw2ewDeb5zcasA4QHPcSVh2+KzbttPQ+RUXCUAr5t+r
0r6gBc5Dy1IPjCFsqsPJXFwqe3RzUb...
-----END RSA PRIVATE KEY-----
requestTimeout: 0 # seconds
rateLimit:
maxRetries: 4
```

### Environment variables

Each one of the configuration values above can be turned into an environment variable name with the `_` (underscore) character:
Expand Down
12 changes: 10 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ module github.com/okta/okta-sdk-golang
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/google/uuid v1.1.1
github.com/jarcoal/httpmock v1.0.4
github.com/kelseyhightower/envconfig v1.3.0
github.com/kr/pretty v0.1.0 // indirect
github.com/lestrrat-go/jwx v0.9.0
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627
github.com/stretchr/testify v1.3.0
github.com/pkg/errors v0.9.1 // indirect
github.com/square/go-jose v2.4.1+incompatible
github.com/square/go-jose/v3 v3.0.0-20200225220504-708a9fe87ddc
github.com/stretchr/testify v1.5.1
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
gopkg.in/square/go-jose.v2 v2.4.1

)

go 1.13
22 changes: 22 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA=
github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/kelseyhightower/envconfig v1.3.0 h1:IvRS4f2VcIQy6j4ORGIf9145T/AsUB+oY8LyvN8BXNM=
Expand All @@ -13,15 +15,35 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/jwx v0.9.0 h1:Fnd0EWzTm0kFrBPzE/PEPp9nzllES5buMkksPMjEKpM=
github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk=
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 h1:pSCLCl6joCFRnjpeojzOpEYs4q7Vditq8fySFG5ap3Y=
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/square/go-jose v2.4.1+incompatible h1:KFYc54wTtgnd3x4B/Y7Zr1s/QaEx2BNzRsB3Hae5LHo=
github.com/square/go-jose v2.4.1+incompatible/go.mod h1:7MxpAF/1WTVUu8Am+T5kNy+t0902CaLWM4Z745MkOa8=
github.com/square/go-jose/v3 v3.0.0-20200225220504-708a9fe87ddc h1:E/YAnZeUG5DNF2fOyRjBCO/SPa3bRIQhFWfMFaBcguw=
github.com/square/go-jose/v3 v3.0.0-20200225220504-708a9fe87ddc/go.mod h1:JbpHhNyeVc538vtj/ECJ3gPYm1VEitNjsLhm4eJQQbg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
2 changes: 2 additions & 0 deletions okta/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
type Cache interface {
Get(key string) *http.Response
Set(key string, value *http.Response)
GetString(key string) string
SetString(key string, value string)
Delete(key string)
Clear()
Has(key string) bool
Expand Down
13 changes: 13 additions & 0 deletions okta/cache/goCache.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ func (c GoCache) Set(key string, value *http.Response) {
c.rootLibrary.Set(key, value, c.ttl)
}

func (c GoCache) GetString(key string) string {
item, found := c.rootLibrary.Get(key)
if found {
return item.(string)
}

return ""
}

func (c GoCache) SetString(key string, value string) {
c.rootLibrary.Set(key, value, c.ttl)
}

func (c GoCache) Delete(key string) {
c.rootLibrary.Delete(key)
}
Expand Down
8 changes: 8 additions & 0 deletions okta/cache/noopCache.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ func (c NoOpCache) Set(key string, value *http.Response) {

}

func (c NoOpCache) GetString(key string) string {
return ""
}

func (c NoOpCache) SetString(key string, value string) {

}

func (c NoOpCache) Delete(key string) {

}
Expand Down
32 changes: 30 additions & 2 deletions okta/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ type config struct {
RateLimit struct {
MaxRetries int32 `yaml:"maxRetries" envconfig:"OKTA_CLIENT_RATE_LIMIT_MAX_RETRIES"`
} `yaml:"rateLimit"`
OrgUrl string `yaml:"orgUrl" envconfig:"OKTA_CLIENT_ORGURL"`
Token string `yaml:"token" envconfig:"OKTA_CLIENT_TOKEN"`
OrgUrl string `yaml:"orgUrl" envconfig:"OKTA_CLIENT_ORGURL"`
Token string `yaml:"token" envconfig:"OKTA_CLIENT_TOKEN"`
AuthorizationMode string `yaml:"authorizationMode" envconfig:"OKTA_CLIENT_AUTHORIZATIONMODE"`
ClientId string `yaml:"clientId" envconfig:"OKTA_CLIENT_CLIENTID"`
Scopes []string `yaml:"scopes" envconfig:"OKTA_CLIENT_SCOPES"`
bretterer marked this conversation as resolved.
Show resolved Hide resolved
PrivateKey string `yaml:"privateKey" envconfig:"OKTA_CLIENT_PRIVATEKEY"`
} `yaml:"client"`
Testing struct {
DisableHttpsCheck bool `yaml:"disableHttpsCheck" envconfig:"OKTA_TESTING_DISABLE_HTTPS_CHECK"`
Expand Down Expand Up @@ -150,3 +154,27 @@ func WithRateLimitMaxRetries(maxRetries int32) ConfigSetter {
c.Okta.Client.RateLimit.MaxRetries = maxRetries
}
}

func WithAuthorizationMode(authzMode string) ConfigSetter {
return func(c *config) {
c.Okta.Client.AuthorizationMode = authzMode
}
}

func WithClientId(clientId string) ConfigSetter {
return func(c *config) {
c.Okta.Client.ClientId = clientId
}
}

func WithScopes(scopes []string) ConfigSetter {
return func(c *config) {
c.Okta.Client.Scopes = scopes
}
}

func WithPrivateKey(privateKey string) ConfigSetter {
return func(c *config) {
c.Okta.Client.PrivateKey = privateKey
}
}
15 changes: 8 additions & 7 deletions okta/okta.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,27 +118,28 @@ func setConfigDefaults(c *config) {
WithUserAgentExtra(""),
WithTestingDisableHttpsCheck(false),
WithRequestTimeout(0),
WithRateLimitMaxRetries(2))
WithRateLimitMaxRetries(2),
WithAuthorizationMode("SSWS"))

for _, confSetter := range conf {
confSetter(c)
}
}

func readConfigFromFile(location string) (*config, error) {
func readConfigFromFile(location string, c *config) (*config, error) {
yamlConfig, err := ioutil.ReadFile(location)

if err != nil {
return nil, err
}

conf := config{}
err = yaml.Unmarshal(yamlConfig, &conf)
// conf := config{}
err = yaml.Unmarshal(yamlConfig, c)
if err != nil {
return nil, err
}

return &conf, err
return c, err
}

func readConfigFromSystem(c config) *config {
Expand All @@ -150,7 +151,7 @@ func readConfigFromSystem(c config) *config {
return &c
}

conf, err := readConfigFromFile(currUser.HomeDir + "/.okta/okta.yaml")
conf, err := readConfigFromFile(currUser.HomeDir+"/.okta/okta.yaml", &c)

if err != nil {
return &c
Expand All @@ -160,7 +161,7 @@ func readConfigFromSystem(c config) *config {
}

func readConfigFromApplication(c config) *config {
conf, err := readConfigFromFile(".okta.yaml")
conf, err := readConfigFromFile(".okta.yaml", &c)

if err != nil {
return &c
Expand Down
Loading