The library implements an Authentication handler for HTTP requests using the Akamai EdgeGrid Authentication scheme for Go. It also currently provides Akamai REST API support for the Akamai Terraform Provider.
This module isn't backward compatible with v1
.
The master
branch isn't representing v1
anymore, it's currently representing the latest v9
. v1
has been moved to a dedicated v1
branch.
You can import the library packages alongside the v1
library without any conflict. For example:
import (
papiv1 "github.com/akamai/AkamaiOPEN-edgegrid-golang/papi-v1"
papi "github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/papi"
)
To use the library, you need to have Go 1.21+ installed on your system.
You can obtain the authentication credentials through an API client. Requests to the API are marked with a timestamp and a signature and are executed immediately.
-
Place your credentials in an EdgeGrid file
~/.edgerc
, in the[default]
section.[default] client_secret = C113nt53KR3TN6N90yVuAgICxIRwsObLi0E67/N8eRN= host = akab-h05tnam3wl42son7nktnlnnx-kbob3i3v.luna.akamaiapis.net access_token = akab-acc35t0k3nodujqunph3w7hzp7-gtm6ij client_token = akab-c113ntt0k3n4qtari252bfxxbsl-yvsdj
-
Import the
edgegrid
package, then use your local.edgerc
by providing the path to your resource file and credentials' section header in theedgegrid.New()
method.package main import ( "fmt" "io" "net/http" "github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid" ) func main() { edgerc, err := edgegrid.New( edgegrid.WithFile("path/to/.edgerc"), edgegrid.WithSection("section-header"), ) ... }
Alternatively, you can pass your credentials to the environment variables. The library will look for the environment variables only if you use the WithEnv()
method and set it to true
. By default, it uses these variables:
AKAMAI_HOST
AKAMAI_CLIENT_TOKEN
AKAMAI_CLIENT_SECRET
AKAMAI_ACCESS_TOKEN
AKAMAI_MAX_BODY
You can define multiple configurations by specifying the credentials' section header as an interfix that you insert between AKAMAI_
and the credential name. The WithEnv()
method uses the credentials' section header configured with the WithSection()
method or it uses default
if the credentials' section header wasn't configured.
For example, when passing ccu
as the credentials' section header in the WithSection()
method, the function will look for AKAMAI_CCU_HOST
.
If the variable doesn't exist but the library was configured to search for it, the function either returns as only partially configured or it falls back to the filing data from the .edgerc
file if you also used the WithFile()
method.
package main
import (
"fmt"
"io"
"net/http"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
)
func main() {
// Load from AKAMAI_CCU_
edgerc, err := edgegrid.New(
edgegrid.WithEnv(true),
edgegrid.WithSection("ccu"),
)
...
}
Import the edgegrid
package to sign your requests. Provide the path to your .edgerc
, your credentials' section header, and the appropriate endpoint information.
package main
import (
"fmt"
"io"
"net/http"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
)
func main() {
edgerc, err := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
client := http.Client{}
req, err := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Accept", "application/json")
edgerc.SignRequest(req)
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
Pass the query parameters in the url after a question mark ("?") at the end of the main URL path.
package main
import (
"fmt"
"io"
"net/http"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
)
func main() {
edgerc, err := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
client := http.Client{}
req, err := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile?authGrants=true¬ifications=true&actions=true", nil)
...
}
Alternatively, you can use one of these methods:
req.URL.Query()
. To add specific query params to an existing query.url.Values{}
. To build a new set of params. Requires importing thenet/url
package.
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
)
func main() {
edgerc, err := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
client := http.Client{}
req, err := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil)
if err != nil {
fmt.Println(err)
return
}
// If appending to an existing query
q := req.URL.Query()
q.Add("authGrants", "true")
q.Add("notifications", "true")
q.Add("actions", "true")
// Or if building a new set of params
q := url.Values{}
q.Add("authGrants", "true")
q.Add("notifications", "true")
q.Add("actions", "true")
req.URL.RawQuery = q.Encode()
fmt.Println(req.URL.String())
// Output:
// /identity-management/v3/user-profile?actions=true&authGrants=true¬ifications=true
edgerc.SignRequest(req)
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
Enter request headers using the req.Header.Add()
method.
Note: You don't need to include the
Content-Type
andAccept
headers. The authentication layer adds these values.
package main
import (
"fmt"
"io"
"net/http"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
)
func main() {
edgerc, err := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
client := http.Client{}
req, err := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Accept", "application/json")
edgerc.SignRequest(req)
...
}
Import the strings
package and provide the request body as an object in the payload
property.
package main
import (
"fmt"
"io"
"net/http"
"strings"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
)
func main() {
edgerc, err := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
payload := strings.NewReader(`{
"contactType": "Billing",
"country": "USA",
"firstName": "John",
"lastName": "Smith",
"preferredLanguage": "English",
"sessionTimeOut": 30,
"timeZone": "GMT",
"phone": "3456788765"
}`)
client := http.Client{}
req, err := http.NewRequest(http.MethodPut, "/identity-management/v3/user-profile/basic-info", payload)
...
}
Enable debugging to get additional information about a request and response.
You may find using fmt.Println()
not sufficient, as the output isn't formatted and is difficult to read.
To pretty-print the HTTP request and response, use these methods:
httputil.DumpRequest()
. To pretty-print the request on the server side.httputil.DumpRequestOut()
. To dump the request on the client side.httputil.DumpResponse()
. To log the server response.
In each of these functions, the second argument is a flag indicating if the body of the request/response should also be returned.
package main
import (
"fmt"
"log"
"net/http"
"net/http/httputil"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
)
func main() {
edgerc, err := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
client := http.Client{}
req, err := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Add("Accept", "application/json")
edgerc.SignRequest(req)
reqDump, err := httputil.DumpRequestOut(req, false)
if err != nil {
log.Fatal(err)
}
fmt.Printf("REQUEST:\n%s", string(reqDump))
res, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
resDump, err := httputil.DumpResponse(res, true)
if err != nil {
log.Fatal(err)
}
fmt.Printf("RESPONSE:\n%s", string(resDump))
}
When making a call with the auth signer approach, you can add the session
package to log additional information about the call. You can also define the struct
for the requested object as a variable in that call, or you can omit the struct
and define the requested object as an any type variable with the any
keyword.
package main
import (
"fmt"
"net/http"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/session"
)
func main() {
edgerc, _ := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
sess, err := session.New(
session.WithSigner(edgerc),
)
if err != nil {
fmt.Println(err)
return
}
var userProfile struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
UserName string `json:"uiUserName"`
TimeZone string `json:"timeZone"`
Country string `json:"country"`
PreferredLanguage string `json:"preferredLanguage"`
SessionTimeOut *int `json:"sessionTimeOut"`
}
req, err := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil)
if err != nil {
fmt.Println(err)
return
}
result, err := sess.Exec(req, &userProfile)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(result, userProfile)
}
The session
package also supports the structured logging interface from github.com/apex
. Thanks to this, you can create a custom logger in one of these ways:
-
Apply a logger globally to the session with the
session.WithLog()
method.Note: This method works also with the SDK approach.
package main import ( "fmt" "net/http" "github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid" "github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/session" "github.com/apex/log" ) type CustomHandler struct { } func (CustomHandler) HandleLog(entry *log.Entry) error { fmt.Printf("#|||### %s ###|||#", entry) // It's a dummy handler. Don't use it in your production code. return nil } func main() { edgerc, _ := edgegrid.New( edgegrid.WithFile("~/.edgerc"), edgegrid.WithSection("default"), ) l := &log.Logger{ Handler: CustomHandler{}, Level: log.InfoLevel, } sess, _ := session.New( session.WithSigner(edgerc), session.WithLog(l), ) var userProfile struct { FirstName string `json:"firstName"` LastName string `json:"lastName"` UserName string `json:"uiUserName"` TimeZone string `json:"timeZone"` Country string `json:"country"` PreferredLanguage string `json:"preferredLanguage"` SessionTimeOut *int `json:"sessionTimeOut"` } req, _ := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil) result, err := sess.Exec(req, &userProfile) if err != nil { fmt.Println(err) return } fmt.Println(result, userProfile) }
-
Overwrite a default logger for a specific request with the
req.WithContext()
method.Note: This method doesn't work with the SDK approach.
package main import ( "fmt" "net/http" "github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid" "github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/session" "github.com/apex/log" ) type CustomHandler struct { } func (CustomHandler) HandleLog(entry *log.Entry) error { fmt.Printf("#|||### %s ###|||#", entry) // It's a dummy handler. Don't use it in your production code. return nil } func main() { edgerc, _ := edgegrid.New( edgegrid.WithFile("~/.edgerc"), edgegrid.WithSection("default"), ) l := &log.Logger{ Handler: CustomHandler{}, Level: log.InfoLevel, } sess, _ := session.New( session.WithSigner(edgerc), ) var userProfile struct { FirstName string `json:"firstName"` LastName string `json:"lastName"` UserName string `json:"uiUserName"` TimeZone string `json:"timeZone"` Country string `json:"country"` PreferredLanguage string `json:"preferredLanguage"` SessionTimeOut *int `json:"sessionTimeOut"` } req, _ := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil) req = req.WithContext(session.ContextWithOptions(req.Context(), session.WithContextLog(l))) result, err := sess.Exec(req, &userProfile) if err != nil { fmt.Println(err) return } fmt.Println(result, userProfile) }
If you don't provide a custom logger, the library will use its default logger included in the session.New()
method.
When using the session
package with the auth signer approach, you can update the context to pass custom request headers in the session.WithContextHeaders()
method.
package main
import (
"fmt"
"net/http"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/session"
)
func main() {
edgerc, _ := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
sess, _ := session.New(
session.WithSigner(edgerc),
)
var userProfile struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
UserName string `json:"uiUserName"`
TimeZone string `json:"timeZone"`
Country string `json:"country"`
PreferredLanguage string `json:"preferredLanguage"`
SessionTimeOut *int `json:"sessionTimeOut"`
}
req, _ := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil)
customHeader := make(http.Header)
customHeader.Set("X-Custom-Header", "some custom value")
req = req.WithContext(session.ContextWithOptions(req.Context(), session.WithContextHeaders(customHeader)))
result, err := sess.Exec(req, &userProfile)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(result, userProfile)
}
With the session
package, you can use the session.WithRetries()
option for creating new sessions with global GET retries. You can set up this option using its default configuration or customize it with these parameters:
RetryMax
. The maximum number of API request retries.RetryWaitMin
. The minimum wait time intime.Duration
between API requests retries.RetryWaitMax
. The maximum wait time intime.Duration
between API requests retries.ExcludedEndpoints
. The list of path expressions defining endpoints which should be excluded from the retry feature.
Note This option works also with the SDK approach.
package main
import (
"fmt"
"net/http"
"time"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/session"
)
func main() {
edgerc, _ := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
// The retries feature with default configuration.
sess, err := session.New(
session.WithSigner(edgerc),
session.WithRetries(session.NewRetryConfig()),
)
// The retries feature with custom configuration.
sess, err := session.New(
session.WithSigner(edgerc),
session.WithRetries(session.RetryConfig{
RetryMax: 5,
RetryWaitMax: time.Minute * 2,
RetryWaitMin: time.Second * 3,
}),
)
if err != nil {
fmt.Println(err)
return
}
var userProfile struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
UserName string `json:"uiUserName"`
TimeZone string `json:"timeZone"`
Country string `json:"country"`
PreferredLanguage string `json:"preferredLanguage"`
SessionTimeOut *int `json:"sessionTimeOut"`
}
req, err := http.NewRequest(http.MethodGet, "/identity-management/v3/user-profile", nil)
if err != nil {
fmt.Println(err)
return
}
result, err := sess.Exec(req, &userProfile)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(result, userProfile)
}
Apart from signing the requests with the edgegrid
package, you can use specific API clients and methods from this library.
To see what methods you can use within a given package, go to the pkg
directory and select the package name. Each package contains:
- A file named after a package name or the package's abbreviated name with a main
interface
. - The main
interface
can contain its own methods or otherinterfaces
with their own methods. struct
blocks distributed across the package's files, each containing a list of parameters you can pass in a given method.
Package | Description |
---|---|
Application Security | Manage security configurations, security policies, match targets, rate policies, and firewall rules. |
Bot Manager | Identify, track, and respond to bot activity on your domain or in your app. |
Certificate Provisioning System | Manage the full life cycle of SSL certificates for your Akamai CDN applications. |
Client Lists | Reduce harmful security attacks by allowing only trusted IP/CIDRs, locations, autonomous system numbers, and TLS fingerprints to access your services and content. |
Cloud Access Manager | Enable cloud origin authentication and securely store and manage your cloud origin credentials as access keys. |
Cloudlets | Solve specific business challenges using value-added apps that complement Akamai's core solutions. |
Cloud Wrapper | Provide your customers with a more consistent user experience by adding a custom caching layer that improves the connection between your cloud infrastructure and the Akamai platform. |
DataStream | Monitor activity on the Akamai platform and send live log data to a destination of your choice. |
Edge DNS | Replace or augment your DNS infrastructure with a cloud-based authoritative DNS solution. |
EdgeGrid | Parse the Akamai .edgerc configuration and sign HTTP requests. |
EdgeGrid Errors | Parse validation errors to make them more readable. |
Edge Hostnames | Manage how requests for your site, app, or content map to Akamai edge servers. |
EdgeWorkers | Execute JavaScript functions at the edge to optimize site performance and customize web experiences. |
Errors | Use utilities for working with errors during JSON data unmarshalling. |
Global Traffic Management | Use load balancing to manage website and mobile performance demands. |
Identity and Access Management | Create users and groups, and define policies that manage access to your Akamai applications. |
Image and Video Manager | Automate image and video delivery optimizations for your website visitors. |
Network Lists | Automate the creation, deployment, and management of lists used in Akamai security products. |
Pointer Record | Create pointers to values of any type. |
Property Manager | Define rules and behaviors that govern your website delivery based on match criteria. |
Session | Manage the base secure HTTP client and requests for Akamai APIs. |
To use the library as an SDK, import the edgegrid
package to sign your requests, the session
package, and a specific package available in this library (for example, iam
) to use the methods included in that package. Provide the path to your .edgerc
, your credentials' section header, and refer to a specific API client and method.
package main
import (
"context"
"fmt"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/iam"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/session"
)
func main() {
edgerc, err := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
if err != nil {
fmt.Println(err)
return
}
sess, err := session.New(
session.WithSigner(edgerc),
)
if err != nil {
fmt.Println(err)
return
}
client := iam.Client(sess)
resp, err := client.GetUser(context.TODO(), iam.GetUserRequest{IdentityID: "A-BC-1234567"})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(resp)
}
You don't need to add any required or optional headers to a request. The authentication layer adds these values automatically for you.
Depending on the method type, use structs
of the provided API struct
to pass query parameters and body data within a request method.
package main
import (
"context"
"fmt"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/edgegrid"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/iam"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/session"
)
func main() {
edgerc, err := edgegrid.New(
edgegrid.WithFile("~/.edgerc"),
edgegrid.WithSection("default"),
)
if err != nil {
fmt.Println(err)
return
}
sess, err := session.New(
session.WithSigner(edgerc),
)
if err != nil {
fmt.Println(err)
return
}
client := iam.Client(sess)
sessTimeOut := 14400
resp, err := client.UpdateUserInfo(
context.TODO(),
iam.UpdateUserInfoRequest{
IdentityID: "A-BC-123456",
User: iam.UserBasicInfo{
Country: "USA",
FirstName: "John",
LastName: "Smith",
Phone: "3456788765",
PreferredLanguage: "English",
SessionTimeOut: &sessTimeOut,
TimeZone: "GMT",
},
},
)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(resp)
}
To report an issue or make a suggestion, create a new GitHub issue.
Copyright 2024 Akamai Technologies, Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use these files except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.