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