github.com/clerkinc/clerk-sdk-go@v1.49.1/clerk/clerk.go (about) 1 package clerk 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "net/url" 12 "strconv" 13 "strings" 14 "time" 15 ) 16 17 const version = "1.49.0" 18 19 const ( 20 ProdUrl = "https://api.clerk.dev/v1/" 21 22 ActorTokensUrl = "actor_tokens" 23 AllowlistsUrl = "allowlist_identifiers" 24 BlocklistsUrl = "blocklist_identifiers" 25 ClientsUrl = "clients" 26 ClientsVerifyUrl = ClientsUrl + "/verify" 27 DomainsURL = "domains" 28 EmailAddressesURL = "email_addresses" 29 EmailsUrl = "emails" 30 InvitationsURL = "invitations" 31 OrganizationsUrl = "organizations" 32 PhoneNumbersURL = "phone_numbers" 33 ProxyChecksURL = "proxy_checks" 34 RedirectURLsUrl = "redirect_urls" 35 SAMLConnectionsUrl = "saml_connections" 36 SessionsUrl = "sessions" 37 TemplatesUrl = "templates" 38 UsersUrl = "users" 39 UsersCountUrl = UsersUrl + "/count" 40 WebhooksUrl = "webhooks" 41 JWTTemplatesUrl = "jwt_templates" 42 ) 43 44 var defaultHTTPClient = &http.Client{Timeout: time.Second * 5} 45 46 type Client interface { 47 NewRequest(method, url string, body ...interface{}) (*http.Request, error) 48 Do(req *http.Request, v interface{}) (*http.Response, error) 49 50 DecodeToken(token string) (*TokenClaims, error) 51 VerifyToken(token string, opts ...VerifyTokenOption) (*SessionClaims, error) 52 53 Allowlists() *AllowlistsService 54 Blocklists() *BlocklistsService 55 Clients() *ClientsService 56 Domains() *DomainsService 57 EmailAddresses() *EmailAddressesService 58 Emails() *EmailService 59 ActorTokens() *ActorTokenService 60 Instances() *InstanceService 61 JWKS() *JWKSService 62 JWTTemplates() *JWTTemplatesService 63 Organizations() *OrganizationsService 64 PhoneNumbers() *PhoneNumbersService 65 ProxyChecks() *ProxyChecksService 66 RedirectURLs() *RedirectURLsService 67 SAMLConnections() *SAMLConnectionsService 68 Sessions() *SessionsService 69 Templates() *TemplatesService 70 Users() *UsersService 71 Webhooks() *WebhooksService 72 Verification() *VerificationService 73 Interstitial() ([]byte, error) 74 75 APIKey() string 76 } 77 78 type service struct { 79 client Client 80 } 81 82 type client struct { 83 client *http.Client 84 baseURL *url.URL 85 jwksCache *jwksCache 86 token string 87 88 allowlists *AllowlistsService 89 blocklists *BlocklistsService 90 clients *ClientsService 91 domains *DomainsService 92 emailAddresses *EmailAddressesService 93 emails *EmailService 94 actorTokens *ActorTokenService 95 instances *InstanceService 96 jwks *JWKSService 97 jwtTemplates *JWTTemplatesService 98 organizations *OrganizationsService 99 phoneNumbers *PhoneNumbersService 100 proxyChecks *ProxyChecksService 101 redirectURLs *RedirectURLsService 102 samlConnections *SAMLConnectionsService 103 sessions *SessionsService 104 templates *TemplatesService 105 users *UsersService 106 webhooks *WebhooksService 107 verification *VerificationService 108 } 109 110 // NewClient creates a new Clerk client. 111 // Because the token supplied will be used for all authenticated requests, 112 // the created client should not be used across different users 113 func NewClient(token string, options ...ClerkOption) (Client, error) { 114 if token == "" { 115 return nil, errors.New("you must provide an API token") 116 } 117 118 defaultBaseURL, err := toURLWithEndingSlash(ProdUrl) 119 if err != nil { 120 return nil, err 121 } 122 123 client := &client{ 124 client: defaultHTTPClient, 125 baseURL: defaultBaseURL, 126 token: token, 127 } 128 129 for _, option := range options { 130 if err = option(client); err != nil { 131 return nil, err 132 } 133 } 134 135 commonService := &service{client: client} 136 client.allowlists = (*AllowlistsService)(commonService) 137 client.blocklists = (*BlocklistsService)(commonService) 138 client.clients = (*ClientsService)(commonService) 139 client.domains = (*DomainsService)(commonService) 140 client.emailAddresses = (*EmailAddressesService)(commonService) 141 client.emails = (*EmailService)(commonService) 142 client.actorTokens = (*ActorTokenService)(commonService) 143 client.instances = (*InstanceService)(commonService) 144 client.jwks = (*JWKSService)(commonService) 145 client.jwtTemplates = (*JWTTemplatesService)(commonService) 146 client.organizations = (*OrganizationsService)(commonService) 147 client.phoneNumbers = (*PhoneNumbersService)(commonService) 148 client.proxyChecks = (*ProxyChecksService)(commonService) 149 client.redirectURLs = (*RedirectURLsService)(commonService) 150 client.samlConnections = (*SAMLConnectionsService)(commonService) 151 client.sessions = (*SessionsService)(commonService) 152 client.templates = (*TemplatesService)(commonService) 153 client.users = (*UsersService)(commonService) 154 client.webhooks = (*WebhooksService)(commonService) 155 client.verification = (*VerificationService)(commonService) 156 157 client.jwksCache = &jwksCache{} 158 159 return client, nil 160 } 161 162 // Deprecated: NewClientWithBaseUrl is deprecated. Use the NewClient instead e.g. NewClient(token, WithBaseURL(baseUrl)) 163 func NewClientWithBaseUrl(token, baseUrl string) (Client, error) { 164 return NewClient(token, WithBaseURL(baseUrl)) 165 } 166 167 // Deprecated: NewClientWithCustomHTTP is deprecated. Use the NewClient instead e.g. NewClient(token, WithBaseURL(urlStr), WithHTTPClient(httpClient)) 168 func NewClientWithCustomHTTP(token, urlStr string, httpClient *http.Client) (Client, error) { 169 return NewClient(token, WithBaseURL(urlStr), WithHTTPClient(httpClient)) 170 } 171 172 func toURLWithEndingSlash(u string) (*url.URL, error) { 173 baseURL, err := url.Parse(u) 174 if err != nil { 175 return nil, err 176 } 177 178 if !strings.HasSuffix(baseURL.Path, "/") { 179 baseURL.Path += "/" 180 } 181 182 return baseURL, err 183 } 184 185 // NewRequest creates an API request. 186 // A relative URL `url` can be specified which is resolved relative to the baseURL of the client. 187 // Relative URLs should be specified without a preceding slash. 188 // The `body` parameter can be used to pass a body to the request. If no body is required, the parameter can be omitted. 189 func (c *client) NewRequest(method, url string, body ...interface{}) (*http.Request, error) { 190 fullUrl, err := c.baseURL.Parse(url) 191 if err != nil { 192 return nil, err 193 } 194 195 var buf io.ReadWriter 196 if len(body) > 0 && body[0] != nil { 197 buf = &bytes.Buffer{} 198 enc := json.NewEncoder(buf) 199 enc.SetEscapeHTML(false) 200 err := enc.Encode(body[0]) 201 if err != nil { 202 return nil, err 203 } 204 } 205 206 req, err := http.NewRequest(method, fullUrl.String(), buf) 207 if err != nil { 208 return nil, err 209 } 210 211 // Add custom header with the current SDK version 212 req.Header.Set("X-Clerk-SDK", fmt.Sprintf("go/%s", version)) 213 214 return req, nil 215 } 216 217 // Do will send the given request using the client `c` on which it is called. 218 // If the response contains a body, it will be unmarshalled in `v`. 219 func (c *client) Do(req *http.Request, v interface{}) (*http.Response, error) { 220 req.Header.Set("Authorization", "Bearer "+c.token) 221 222 resp, err := c.client.Do(req) 223 if err != nil { 224 return nil, err 225 } 226 defer resp.Body.Close() 227 228 err = checkForErrors(resp) 229 if err != nil { 230 return resp, err 231 } 232 233 if resp.Body != nil && v != nil { 234 body, err := ioutil.ReadAll(resp.Body) 235 if err != nil { 236 return resp, err 237 } 238 239 err = json.Unmarshal(body, &v) 240 if err != nil { 241 return resp, err 242 } 243 } 244 245 return resp, nil 246 } 247 248 func checkForErrors(resp *http.Response) error { 249 if c := resp.StatusCode; c >= 200 && c < 400 { 250 return nil 251 } 252 253 errorResponse := &ErrorResponse{Response: resp} 254 255 data, err := ioutil.ReadAll(resp.Body) 256 if err == nil && data != nil { 257 // it's ok if we cannot unmarshal to Clerk's error response 258 _ = json.Unmarshal(data, errorResponse) 259 } 260 261 return errorResponse 262 } 263 264 func (c *client) Allowlists() *AllowlistsService { 265 return c.allowlists 266 } 267 268 func (c *client) Blocklists() *BlocklistsService { 269 return c.blocklists 270 } 271 272 func (c *client) Clients() *ClientsService { 273 return c.clients 274 } 275 276 func (c *client) Domains() *DomainsService { 277 return c.domains 278 } 279 280 func (c *client) EmailAddresses() *EmailAddressesService { 281 return c.emailAddresses 282 } 283 284 func (c *client) Emails() *EmailService { 285 return c.emails 286 } 287 288 func (c *client) ActorTokens() *ActorTokenService { 289 return c.actorTokens 290 } 291 292 func (c *client) Instances() *InstanceService { 293 return c.instances 294 } 295 296 func (c *client) JWKS() *JWKSService { 297 return c.jwks 298 } 299 300 func (c *client) JWTTemplates() *JWTTemplatesService { 301 return c.jwtTemplates 302 } 303 304 func (c *client) Organizations() *OrganizationsService { 305 return c.organizations 306 } 307 308 func (c *client) PhoneNumbers() *PhoneNumbersService { 309 return c.phoneNumbers 310 } 311 312 func (c *client) ProxyChecks() *ProxyChecksService { 313 return c.proxyChecks 314 } 315 316 func (c *client) RedirectURLs() *RedirectURLsService { 317 return c.redirectURLs 318 } 319 320 func (c *client) SAMLConnections() *SAMLConnectionsService { 321 return c.samlConnections 322 } 323 324 func (c *client) Sessions() *SessionsService { 325 return c.sessions 326 } 327 328 func (c *client) Templates() *TemplatesService { 329 return c.templates 330 } 331 332 func (c *client) Users() *UsersService { 333 return c.users 334 } 335 336 func (c *client) Webhooks() *WebhooksService { 337 return c.webhooks 338 } 339 340 func (c *client) Verification() *VerificationService { 341 return c.verification 342 } 343 344 func (c *client) APIKey() string { 345 return c.token 346 } 347 348 func (c *client) Interstitial() ([]byte, error) { 349 req, err := c.NewRequest("GET", "internal/interstitial") 350 if err != nil { 351 return nil, err 352 } 353 req.Header.Set("Authorization", "Bearer "+c.token) 354 355 resp, err := c.client.Do(req) 356 if err != nil { 357 return nil, err 358 } 359 defer resp.Body.Close() 360 361 interstitial, err := ioutil.ReadAll(resp.Body) 362 if err != nil { 363 return interstitial, err 364 } 365 366 return interstitial, nil 367 } 368 369 type PaginationParams struct { 370 Limit *int 371 Offset *int 372 } 373 374 func addPaginationParams(query url.Values, params PaginationParams) { 375 if params.Limit != nil { 376 query.Set("limit", strconv.Itoa(*params.Limit)) 377 } 378 if params.Offset != nil { 379 query.Set("offset", strconv.Itoa(*params.Offset)) 380 } 381 }