• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.vscode/H06-Jun-2020-

okta/H06-Jun-2020-

openapi/H06-Jun-2020-

tests/H06-Jun-2020-

vendor/github.com/H06-Jun-2020-

.editorconfigH A D06-Jun-2020284

.gitignoreH A D06-Jun-202014

.travis.ymlH A D06-Jun-202081

CHANGELOG.mdH A D06-Jun-20201.8 KiB

CONTRIBUTING.mdH A D06-Jun-20205.8 KiB

Gopkg.lockH A D06-Jun-20201.2 KiB

Gopkg.tomlH A D06-Jun-2020897

LICENSE.mdH A D06-Jun-202010.2 KiB

MakefileH A D06-Jun-20202 KiB

NOTICE.mdH A D06-Jun-2020649

README.mdH A D06-Jun-202017.9 KiB

go.modH A D06-Jun-2020480

go.sumH A D06-Jun-20206.8 KiB

README.md

1[<img src="https://devforum.okta.com/uploads/oktadev/original/1X/bf54a16b5fda189e4ad2706fb57cbb7a1e5b8deb.png" align="right" width="256px"/>](https://devforum.okta.com/)
2[![Build Status](https://img.shields.io/travis/okta/okta-sdk-golang.svg?logo=travis)](https://travis-ci.org/okta/okta-sdk-golang)
3[![License](https://img.shields.io/github/license/okta/okta-sdk-golang.svg)](https://opensource.org/licenses/Apache-2.0)
4[![Support](https://img.shields.io/badge/support-Developer%20Forum-blue.svg)][devforum]
5[![API Reference](https://img.shields.io/badge/docs-reference-lightgrey.svg)][sdkapiref]
6
7# Okta Golang management SDK
8* [Release status](#release-status)
9* [Need help?](#need-help)
10* [Getting started](#getting-started)
11* [Usage guide](#usage-guide)
12* [Configuration reference](#configuration-reference)
13* [Building the SDK](#building-the-sdk)
14* [Contributing](#contributing)
15
16This repository contains the Okta management SDK for Golang. This SDK can be used in your server-side code to interact with the Okta management API and
17
18* Create and update users with the [Users API](https://developer.okta.com/docs/api/resources/users)
19* Add security factors to users with the [Factors API](https://developer.okta.com/docs/api/resources/factors)
20* Manage groups with the [Groups API](https://developer.okta.com/docs/api/resources/groups)
21* Manage applications with the [Apps API](https://developer.okta.com/docs/api/resources/apps)
22* Much more!
23
24We also publish these libraries for Golang:
25
26* [JWT Verifier](https://github.com/okta/okta-jwt-verifier-golang)
27
28You can learn more on the [Okta + Golang](lang-landing) page in our documentation.
29
30## Release status
31
32This library uses semantic versioning and follows Okta's [library version policy](https://developer.okta.com/code/library-versions/).
33
34| Version | Status                             |
35| ------- | ---------------------------------- |
36| 0.x     |  :warning: Beta Release (Retired)  |
37| 1.x     |  :warning: Retiring on 2021-03-04  |
38| 2.x     |  :heavy_check_mark: Release        |
39
40The latest release can always be found on the [releases page][github-releases].
41
42## Need help?
43
44If you run into problems using the SDK, you can
45
46* Ask questions on the [Okta Developer Forums][devforum]
47* Post [issues][github-issues] here on GitHub (for code errors)
48
49## Getting started
50
51To install the Okta Golang SDK in your project:
52
53Version 2.x (Release)
54run `go get github.com/okta/okta-sdk-golang/v2/okta`
55
56You'll also need
57
58* An Okta account, called an _organization_ (sign up for a free [developer organization](https://developer.okta.com/signup) if you need one)
59* An [API token](https://developer.okta.com/docs/api/getting_started/getting_a_token)
60
61Construct a client instance by passing it your Okta domain name and API token:
62
63```
64client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
65```
66
67Hard-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.
68
69## Upgrading to 2.0.x
70The main purpose of this version is to include all documented, application/json endpoints
71to the SDK. During this update we have made many changes to method names, as well as method signatures.
72
73### Context
74Every method that calls the API now has the ability to pass `context.Context` to it as the first parameter. If you do not have a context or do not know which context to use, you can pass `context.TODO()` to the methods.
75
76### Method changes
77We have spent time during this update making sure we become a little more uniform with naming of methods. This will require you to update some of your calls to the SDK with the new names.
78
79All methods now specify the `Accept` and `Content-Type` headers when creating a new request. This allows for future use of the SDK to handle multiple `Accept` types.
80
81### OAuth 2.0
82
83Okta 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.
84
85This 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.
86
87When 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:
88
89```
90client, _ := okta.NewClient(context,
91  okta.WithAuthorizationMode("PrivateKey"),
92  okta.WithClientId("{{clientId}}),
93  okta.WithScopes(([]string{"okta.users.manage"})),
94  okta.WithPrivateKey({{PEM PRIVATE KEY BLOCK}})
95)
96```
97
98### Extending the Client
99When calling `okta.NewClient()` we allow for you to pass custom instances of `http.Client` and `cache.Cache`.
100
101```
102myClient := &http.Client{}
103
104myCache := NewCustomCacheDriver()
105
106client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"), okta.WithHttpClient(myClient), okta.WithCacheManager(myCache))
107```
108
109
110### Extending or Creating New Cache Manager
111You can create a custom cache driver by implementing `cache.Cache`
112
113```
114type CustomCacheDriver struct {
115}
116
117func NewCustomCacheDriver() Cache {
118	return CustomCacheDriver{}
119}
120
121func (c CustomCacheDriver) Get(key string) *http.Response {
122	return nil
123}
124
125func (c CustomCacheDriver) Set(key string, value *http.Response) {}
126
127func (c CustomCacheDriver) Delete(key string) {}
128
129func (c CustomCacheDriver) Clear() {}
130
131func (c CustomCacheDriver) Has(key string) bool {
132	return false
133}
134```
135
136### Refreshing Cache for Specific Call
137If you have an issue where you do a `GET`, then a `DELETE`, and then re-issue a `GET` to the original endpoint, you may have an issue with the cache returning with the deleted resource. An example of this is listing applicaiton users, delete and application user, and them listing them again.
138
139You can solve this by running `client.GetRequestExecutor().RefreshNext()` before your second `ListApplicationUsers` call, which will tell the call to delete the cache for this endpoint and make a new call.
140
141```go
142appUserList, _, _ = client.Application.ListApplicationUsers(context.TODO(), appId, nil)
143
144client.Application.DeleteApplicationUser(context.TODO(), appId, appUser.Id, nil)
145
146client.GetRequestExecutor().RefreshNext()
147appUserList, _, _ = client.Application.ListApplicationUsers(context.TODO(), appId, nil)
148```
149
150### Pagination
151If your request comes back with more than the default or set limit, you can request the next page.
152
153Exmaple of listing users 1 at a time:
154```go
155query := query.NewQueryParams(query.WithLimit(1))
156users, resp, err := client.User.ListUsers(ctx, query)
157// Do something with your users until you run out of users to iterate.
158if resp.HasNextPage() {
159  var nextUserSet []*okta.User
160  resp, err = resp.Next(ctx, &nextUserSet)
161}
162```
163
164## Usage guide
165
166These examples will help you understand how to use this library. You can also browse the full [API reference documentation][sdkapiref].
167
168Once you initialize a `client`, you can call methods to make requests to the Okta API. Most methods are grouped by the API endpoint they belong to. For example, methods that call the [Users API](https://developer.okta.com/docs/api/resources/users) are organized under `client.User`.
169
170### Authenticate a User
171This library should only be used with the Okta management API. To call the [Authentication API](https://developer.okta.com/docs/api/resources/authn), you should construct your own HTTP requests.
172
173### Get a User
174```
175client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
176user, resp, err := client.User.GetUser(user.Id, nil)
177```
178
179### List all Users
180```
181client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
182users, resp, err := client.User.ListUsers()
183```
184
185### Filter or search for Users
186```
187client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
188
189filter := query.NewQueryParams(query.WithFilter("status eq \"ACTIVE\""))
190
191users, resp, err := client.User.ListUsers(filter)
192```
193
194### Create a User
195```
196client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
197
198p := &okta.PasswordCredential{
199		Value: "Abcd1234",
200}
201uc := &okta.UserCredentials{
202		Password: p,
203}
204profile := okta.UserProfile{}
205profile["firstName"] = "John"
206profile["lastName"] = "Activate"
207profile["email"] = "john-activate@example.com"
208profile["login"] = "john-activate@example.com"
209u := &okta.User{
210		Credentials: uc,
211		Profile:     &profile,
212}
213
214user, resp, err := client.User.CreateUser(*u, nil)
215```
216
217### Update a User
218```
219client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
220
221newProfile := *user.Profile
222newProfile["nickName"] = "Batman"
223updatedUser := &okta.User{
224  Profile: &newProfile,
225}
226user, resp, err := client.User.UpdateUser(user.Id, *updatedUser, nil)
227```
228
229### Get and set custom attributes
230Custom attributes must first be defined in the Okta profile editor. Then, you can work with custom attributes on a user:
231```
232client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
233user, resp, err := client.User.GetUser(user.Id, nil)
234
235nickName = user.Profile["nickName"]
236```
237
238### Remove a User
239You must first deactivate the user, and then you can delete the user.
240```
241client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
242resp, err := client.User.DeactivateUser(user.Id, nil)
243
244resp, err := client.User.DeactivateOrDeleteUser(user.Id, nil)
245```
246
247### List a User's Groups
248```
249client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
250
251groups, resp, err := client.User.ListUserGroups(user.Id, nil)
252```
253
254### Create a Group
255```
256client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
257
258gp := &okta.GroupProfile{
259  Name: "Get Test Group",
260}
261g := &okta.Group{
262  Profile: gp,
263}
264group, resp, err := client.Group.CreateGroup(*g, nil)
265```
266
267### Add a User to a Group
268```
269client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
270
271resp, err := client.Group.AddUserToGroup(group.Id, user.Id, nil)
272```
273
274### List a User's enrolled Factors
275```
276client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
277
278allowedFactors, resp, err := client.Factor.ListSupportedFactors(user.Id)
279```
280
281### Enroll a User in a new Factor
282```
283client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
284
285factorProfile := okta.NewSmsFactorProfile()
286factorProfile.PhoneNumber = "5551234567"
287
288factor := okta.NewSmsFactor()
289factor.Profile = factorProfile
290
291addedFactor, resp, err := client.Factor.AddFactor(user.Id, factor, nil)
292```
293
294### Activate a Factor
295```
296client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
297
298factor, resp, err := client.Factor.ActivateFactor(user.Id, factor.Id, nil)
299```
300
301### Verify a Factor
302```
303client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
304
305verifyFactorRequest := okta.VerifyFactorRequest{
306  PassCode: "123456"
307}
308verifyFactorResp, resp, err := client.Factor.VerifyFactor(user.Id, factor.Id, verifyFactorRequest, nil)
309```
310
311### List all Applications
312```
313client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
314
315applications, resp, err := client.Application.ListApplications(nil)
316
317//applications will need to be cast from the interface into its concrete form before you can use it.
318for _, a := range applications {
319		if a.(*okta.Application).Name == "bookmark" {
320			if a.(*okta.Application).Id == app2.(okta.BookmarkApplication).Id {
321				application :=  *a.(*okta.BookmarkApplication) //This will cast it to a Bookmark Application
322			}
323		}
324		// continue for each type you want to work with.
325	}
326```
327
328### Get an Application
329```
330client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
331
332//Getting a Basic Auth Application
333application, resp, err = client.Application.GetApplication(appId, okta.NewBasicAuthApplication(), nil)
334
335//To use the application, you must cast it to the type.
336app := application.(*okta.BasicAuthApplication)
337```
338
339### Create a SWA Application
340```
341client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
342
343swaAppSettingsApp := newSwaApplicationSettingsApplication()
344swaAppSettingsApp.ButtonField = "btn-login"
345swaAppSettingsApp.PasswordField = "txtbox-password"
346swaAppSettingsApp.UsernameField = "txtbox-username"
347swaAppSettingsApp.Url = "https://example.com/login.html"
348swaAppSettingsApp.LoginUrlRegex = "REGEX_EXPRESSION"
349
350swaAppSettings := newSwaApplicationSettings()
351swaAppSettings.App = &swaAppSettingsApp
352
353swaApp := newSwaApplication()
354swaApp.Label = "Test App"
355swaApp.Settings = &swaAppSettings
356
357application, resp, err := client.Application.CreateApplication(swaApp, nil)
358```
359
360### Call other API endpoints
361Not every API endpoint is represented by a method in this library. You can call any Okta management API endpoint using this generic syntax:
362```
363client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
364
365url := "https://golang.oktapreview.com/api/v1/authorizationServers
366
367type Signing struct {
368	RotationMode string     `json:"rotationMode,omitempty"`
369	LastRotated  *time.Time `json:"lastRotated,omitempty"`
370	NextRotation *time.Time `json:"nextRotation,omitempty"`
371	Kid          string     `json:"kid,omitempty"`
372}
373
374type Credentials struct {
375	Signing *Signing `json:"signing,omitempty"`
376}
377
378type AuthorizationServer struct {
379	Id          string       `json:"id,omitempty"`
380	Name        string       `json:"name,omitempty"`
381	Description string       `json:"description,omitempty"`
382	Audiences   []string     `json:"audiences,omitempty"`
383	Issuer      string       `json:"issuer,omitempty"`
384	IssuerMode  string       `json:"issuerMode,omitempty"`
385	Status      string       `json:"status,omitempty"`
386	Created     *time.Time   `json:"created,omitempty"`
387	LastUpdated *time.Time   `json:"lastUpdated,omitempty"`
388	Credentials *Credentials `json:"credentials,omitempty"`
389	Embedded    interface{}  `json:"_embedded,omitempty"`
390	Links       interface{}  `json:"_links,omitempty"`
391}
392
393as := AuthorizationServer{
394  Name:        "Sample Authorization Server",
395  Description: "Sample Authorization Server description",
396  Audiences:   []string{"api://default"},
397}
398
399req, err := m.client.requestExecutor.NewRequest("POST", url, as)
400if err != nil {
401  return nil, nil, err
402}
403
404var authServer *AuthorizationServer
405resp, err := m.client.requestExecutor.Do(req, &authServer)
406if err != nil {
407  return nil, resp, err
408}
409return authServer, resp, nil
410```
411
412### Access Request Executor
413If you need to gain access to the request executor, we have provided a method off the `Client` to do so.
414
415```go
416re := client.GetRequestExecutor()
417```
418
419Doing this will provide you with the ability to create your own requests for the Okta API and call the `Do` method that handles all of the headers for you based on the configuration.
420
421## Configuration reference
422
423This library looks for configuration in the following sources:
424
4250. An `okta.yaml` file in a `.okta` folder in the current user's home directory (`~/.okta/okta.yaml` or `%userprofile\.okta\okta.yaml`)
4260. A `.okta.yaml` file in the application or project's root directory
4270. Environment variables
4280. Configuration explicitly passed to the constructor (see the example in [Getting started](#getting-started))
429
430Higher 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.
431
432### YAML configuration
433
434When you use an API Token instead of OAuth 2.0 the full YAML configuration looks like:
435
436```yaml
437okta:
438  client:
439    connectionTimeout: 30 # seconds
440    orgUrl: "https://{yourOktaDomain}"
441    proxy:
442      port: null
443      host: null
444      username: null
445      password: null
446    token: {apiToken}
447```
448
449When you use OAuth 2.0 the full YAML configuration looks like:
450
451```yaml
452okta:
453  client:
454    connectionTimeout: 30 # seconds
455    orgUrl: "https://{yourOktaDomain}"
456    proxy:
457      port: null
458      host: null
459      username: null
460      password: null
461    authorizationMode: "PrivateKey"
462    clientId: "{yourClientId}"
463    scopes:
464      - scope.1
465      - scope.2
466    privateKey: |
467        -----BEGIN RSA PRIVATE KEY-----
468        MIIEogIBAAKCAQEAl4F5CrP6Wu2kKwH1Z+CNBdo0iteHhVRIXeHdeoqIB1iXvuv4
469        THQdM5PIlot6XmeV1KUKuzw2ewDeb5zcasA4QHPcSVh2+KzbttPQ+RUXCUAr5t+r
470        0r6gBc5Dy1IPjCFsqsPJXFwqe3RzUb...
471        -----END RSA PRIVATE KEY-----
472    requestTimeout: 0 # seconds
473    rateLimit:
474      maxRetries: 4
475```
476
477### Environment variables
478
479Each one of the configuration values above can be turned into an environment variable name with the `_` (underscore) character:
480
481* `OKTA_CLIENT_CONNECTIONTIMEOUT`
482* `OKTA_CLIENT_TOKEN`
483* and so on
484
485## Building the SDK
486
487In most cases, you won't need to build the SDK from source. If you want to build it yourself, you'll need these prerequisites:
488
489- Clone the repo
490- Run `make build` from the root of the project
491
492## Contributing
493
494We're happy to accept contributions and PRs! Please see the [contribution guide](/okta/okta-sdk-golang/blob/master/CONTRIBUTING.md) to understand how to structure a contribution.
495
496
497[devforum]: https://devforum.okta.com/
498[sdkapiref]: https://godoc.org/github.com/okta/okta-sdk-golang/okta
499[lang-landing]: https://developer.okta.com/code/go/
500[github-issues]: /okta/okta-sdk-golang/issues
501[github-releases]: /okta/okta-sdk-golang/releases
502