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![Beta Release](https://img.shields.io/badge/Beta-Unstable-yellow.svg)
4[![License](https://img.shields.io/github/license/okta/okta-sdk-golang.svg)](https://opensource.org/licenses/Apache-2.0)
5[![Support](https://img.shields.io/badge/support-Developer%20Forum-blue.svg)][devforum]
6[![API Reference](https://img.shields.io/badge/docs-reference-lightgrey.svg)][sdkapiref]
7
8# Okta Golang management SDK
9* [Release status](#release-status)
10* [Need help?](#need-help)
11* [Getting started](#getting-started)
12* [Usage guide](#usage-guide)
13* [Configuration reference](#configuration-reference)
14* [Building the SDK](#building-the-sdk)
15* [Contributing](#contributing)
16
17This 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
18
19* Create and update users with the [Users API](https://developer.okta.com/docs/api/resources/users)
20* Add security factors to users with the [Factors API](https://developer.okta.com/docs/api/resources/factors)
21* Manage groups with the [Groups API](https://developer.okta.com/docs/api/resources/groups)
22* Manage applications with the [Apps API](https://developer.okta.com/docs/api/resources/apps)
23* Much more!
24
25We also publish these libraries for Golang:
26
27* [JWT Verifier](https://github.com/okta/okta-jwt-verifier-golang)
28
29You can learn more on the [Okta + Golang](lang-landing) page in our documentation.
30
31## Release status
32
33This library uses semantic versioning and follows Okta's [library version policy](https://developer.okta.com/code/library-versions/).
34
35| Version | Status |
36| ------- | ------------------------- |
37| 0.x | ⚠️ Beta Release (Retired)|
38| 1.x | ✔️ 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, run `go get github.com/okta/okta-sdk-golang/okta`
52
53You'll also need
54
55* An Okta account, called an _organization_ (sign up for a free [developer organization](https://developer.okta.com/signup) if you need one)
56* An [API token](https://developer.okta.com/docs/api/getting_started/getting_a_token)
57
58Construct a client instance by passing it your Okta domain name and API token:
59
60```
61client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
62```
63
64Hard-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.
65
66### OAuth 2.0
67
68Okta 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.
69
70This 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.
71
72When 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:
73
74```
75client, _ := okta.NewClient(context,
76 okta.WithAuthorizationMode("PrivateKey"),
77 okta.WithClientId("{{clientId}}),
78 okta.WithScopes(([]string{"okta.users.manage"})),
79 okta.WithPrivateKey({{PEM PRIVATE KEY BLOCK}})
80)
81```
82
83### Extending the Client
84When calling `okta.NewClient()` we allow for you to pass custom instances of `http.Client` and `cache.Cache`.
85
86```
87myClient := &http.Client{}
88
89myCache := NewCustomCacheDriver()
90
91client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"), okta.WithHttpClient(myClient), okta.WithCacheManager(myCache))
92```
93
94
95### Extending or Creating New Cache Manager
96You can create a custom cache driver by implementing `cache.Cache`
97
98```
99type CustomCacheDriver struct {
100}
101
102func NewCustomCacheDriver() Cache {
103 return CustomCacheDriver{}
104}
105
106func (c CustomCacheDriver) Get(key string) *http.Response {
107 return nil
108}
109
110func (c CustomCacheDriver) Set(key string, value *http.Response) {}
111
112func (c CustomCacheDriver) Delete(key string) {}
113
114func (c CustomCacheDriver) Clear() {}
115
116func (c CustomCacheDriver) Has(key string) bool {
117 return false
118}
119```
120
121## Usage guide
122
123These examples will help you understand how to use this library. You can also browse the full [API reference documentation][sdkapiref].
124
125Once 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`.
126
127### Authenticate a User
128This 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.
129
130### Get a User
131```
132client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
133user, resp, err := client.User.GetUser(user.Id, nil)
134```
135
136### List all Users
137```
138client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
139users, resp, err := client.User.ListUsers()
140```
141
142### Filter or search for Users
143```
144client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
145
146filter := query.NewQueryParams(query.WithFilter("status eq \"ACTIVE\""))
147
148users, resp, err := client.User.ListUsers(filter)
149```
150
151### Create a User
152```
153client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
154
155p := &okta.PasswordCredential{
156 Value: "Abcd1234",
157}
158uc := &okta.UserCredentials{
159 Password: p,
160}
161profile := okta.UserProfile{}
162profile["firstName"] = "John"
163profile["lastName"] = "Activate"
164profile["email"] = "john-activate@example.com"
165profile["login"] = "john-activate@example.com"
166u := &okta.User{
167 Credentials: uc,
168 Profile: &profile,
169}
170
171user, resp, err := client.User.CreateUser(*u, nil)
172```
173
174### Update a User
175```
176client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
177
178newProfile := *user.Profile
179newProfile["nickName"] = "Batman"
180updatedUser := &okta.User{
181 Profile: &newProfile,
182}
183user, resp, err := client.User.UpdateUser(user.Id, *updatedUser, nil)
184```
185
186### Get and set custom attributes
187Custom attributes must first be defined in the Okta profile editor. Then, you can work with custom attributes on a user:
188```
189client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
190user, resp, err := client.User.GetUser(user.Id, nil)
191
192nickName = user.Profile["nickName"]
193```
194
195### Remove a User
196You must first deactivate the user, and then you can delete the user.
197```
198client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
199resp, err := client.User.DeactivateUser(user.Id, nil)
200
201resp, err := client.User.DeactivateOrDeleteUser(user.Id, nil)
202```
203
204### List a User's Groups
205```
206client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
207
208groups, resp, err := client.User.ListUserGroups(user.Id, nil)
209```
210
211### Create a Group
212```
213client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
214
215gp := &okta.GroupProfile{
216 Name: "Get Test Group",
217}
218g := &okta.Group{
219 Profile: gp,
220}
221group, resp, err := client.Group.CreateGroup(*g, nil)
222```
223
224### Add a User to a Group
225```
226client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
227
228resp, err := client.Group.AddUserToGroup(group.Id, user.Id, nil)
229```
230
231### List a User's enrolled Factors
232```
233client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
234
235allowedFactors, resp, err := client.Factor.ListSupportedFactors(user.Id)
236```
237
238### Enroll a User in a new Factor
239```
240client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
241
242factorProfile := okta.NewSmsFactorProfile()
243factorProfile.PhoneNumber = "5551234567"
244
245factor := okta.NewSmsFactor()
246factor.Profile = factorProfile
247
248addedFactor, resp, err := client.Factor.AddFactor(user.Id, factor, nil)
249```
250
251### Activate a Factor
252```
253client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
254
255factor, resp, err := client.Factor.ActivateFactor(user.Id, factor.Id, nil)
256```
257
258### Verify a Factor
259```
260client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
261
262verifyFactorRequest := okta.VerifyFactorRequest{
263 PassCode: "123456"
264}
265verifyFactorResp, resp, err := client.Factor.VerifyFactor(user.Id, factor.Id, verifyFactorRequest, nil)
266```
267
268### List all Applications
269```
270client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
271
272applications, resp, err := client.Application.ListApplications(nil)
273
274//applications will need to be cast from the interface into its concrete form before you can use it.
275for _, a := range applications {
276 if a.(*okta.Application).Name == "bookmark" {
277 if a.(*okta.Application).Id == app2.(okta.BookmarkApplication).Id {
278 application := *a.(*okta.BookmarkApplication) //This will cast it to a Bookmark Application
279 }
280 }
281 // continue for each type you want to work with.
282 }
283```
284
285### Get an Application
286```
287client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
288
289//Getting a Basic Auth Application
290application, resp, err = client.Application.GetApplication(appId, okta.NewBasicAuthApplication(), nil)
291
292//To use the application, you must cast it to the type.
293app := application.(*okta.BasicAuthApplication)
294```
295
296### Create a SWA Application
297```
298client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
299
300swaAppSettingsApp := newSwaApplicationSettingsApplication()
301swaAppSettingsApp.ButtonField = "btn-login"
302swaAppSettingsApp.PasswordField = "txtbox-password"
303swaAppSettingsApp.UsernameField = "txtbox-username"
304swaAppSettingsApp.Url = "https://example.com/login.html"
305swaAppSettingsApp.LoginUrlRegex = "REGEX_EXPRESSION"
306
307swaAppSettings := newSwaApplicationSettings()
308swaAppSettings.App = &swaAppSettingsApp
309
310swaApp := newSwaApplication()
311swaApp.Label = "Test App"
312swaApp.Settings = &swaAppSettings
313
314application, resp, err := client.Application.CreateApplication(swaApp, nil)
315```
316
317### Call other API endpoints
318Not every API endpoint is represented by a method in this library. You can call any Okta management API endpoint using this generic syntax:
319```
320client := okta.NewClient(context, okta.WithOrgUrl("https://{yourOktaDomain}"), okta.WithToken("{apiToken}"))
321
322url := "https://golang.oktapreview.com/api/v1/authorizationServers
323
324type Signing struct {
325 RotationMode string `json:"rotationMode,omitempty"`
326 LastRotated *time.Time `json:"lastRotated,omitempty"`
327 NextRotation *time.Time `json:"nextRotation,omitempty"`
328 Kid string `json:"kid,omitempty"`
329}
330
331type Credentials struct {
332 Signing *Signing `json:"signing,omitempty"`
333}
334
335type AuthorizationServer struct {
336 Id string `json:"id,omitempty"`
337 Name string `json:"name,omitempty"`
338 Description string `json:"description,omitempty"`
339 Audiences []string `json:"audiences,omitempty"`
340 Issuer string `json:"issuer,omitempty"`
341 IssuerMode string `json:"issuerMode,omitempty"`
342 Status string `json:"status,omitempty"`
343 Created *time.Time `json:"created,omitempty"`
344 LastUpdated *time.Time `json:"lastUpdated,omitempty"`
345 Credentials *Credentials `json:"credentials,omitempty"`
346 Embedded interface{} `json:"_embedded,omitempty"`
347 Links interface{} `json:"_links,omitempty"`
348}
349
350as := AuthorizationServer{
351 Name: "Sample Authorization Server",
352 Description: "Sample Authorization Server description",
353 Audiences: []string{"api://default"},
354}
355
356req, err := m.client.requestExecutor.NewRequest("POST", url, as)
357if err != nil {
358 return nil, nil, err
359}
360
361var authServer *AuthorizationServer
362resp, err := m.client.requestExecutor.Do(req, &authServer)
363if err != nil {
364 return nil, resp, err
365}
366return authServer, resp, nil
367```
368
369## Configuration reference
370
371This library looks for configuration in the following sources:
372
3730. An `okta.yaml` file in a `.okta` folder in the current user's home directory (`~/.okta/okta.yaml` or `%userprofile\.okta\okta.yaml`)
3740. A `.okta.yaml` file in the application or project's root directory
3750. Environment variables
3760. Configuration explicitly passed to the constructor (see the example in [Getting started](#getting-started))
377
378Higher 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.
379
380### YAML configuration
381
382When you use an API Token instead of OAuth 2.0 the full YAML configuration looks like:
383
384```yaml
385okta:
386 client:
387 connectionTimeout: 30 # seconds
388 orgUrl: "https://{yourOktaDomain}"
389 proxy:
390 port: null
391 host: null
392 username: null
393 password: null
394 token: {apiToken}
395```
396
397When you use OAuth 2.0 the full YAML configuration looks like:
398
399```yaml
400okta:
401 client:
402 connectionTimeout: 30 # seconds
403 orgUrl: "https://{yourOktaDomain}"
404 proxy:
405 port: null
406 host: null
407 username: null
408 password: null
409 authorizationMode: "PrivateKey"
410 clientId: "{yourClientId}"
411 scopes:
412 - scope.1
413 - scope.2
414 privateKey: |
415 -----BEGIN RSA PRIVATE KEY-----
416 MIIEogIBAAKCAQEAl4F5CrP6Wu2kKwH1Z+CNBdo0iteHhVRIXeHdeoqIB1iXvuv4
417 THQdM5PIlot6XmeV1KUKuzw2ewDeb5zcasA4QHPcSVh2+KzbttPQ+RUXCUAr5t+r
418 0r6gBc5Dy1IPjCFsqsPJXFwqe3RzUb...
419 -----END RSA PRIVATE KEY-----
420 requestTimeout: 0 # seconds
421 rateLimit:
422 maxRetries: 4
423```
424
425### Environment variables
426
427Each one of the configuration values above can be turned into an environment variable name with the `_` (underscore) character:
428
429* `OKTA_CLIENT_CONNECTIONTIMEOUT`
430* `OKTA_CLIENT_TOKEN`
431* and so on
432
433## Building the SDK
434
435In most cases, you won't need to build the SDK from source. If you want to build it yourself, you'll need these prerequisites:
436
437- Clone the repo
438- Run `make build` from the root of the project
439
440## Contributing
441
442We're happy to accept contributions and PRs! Please see the [contribution guide](contributing.md) to understand how to structure a contribution.
443
444
445[devforum]: https://devforum.okta.com/
446[sdkapiref]: https://godoc.org/github.com/okta/okta-sdk-golang/okta
447[lang-landing]: https://developer.okta.com/code/golang/
448[github-issues]: /issues
449[github-releases]: /releases
450