Skip to content

Commit f22d567

Browse files
authored
feat(client-secret): Add example on how to use client secret (#4)
1 parent 330427a commit f22d567

File tree

4 files changed

+50
-8
lines changed

4 files changed

+50
-8
lines changed

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,31 @@ This example code demonstrates how to use AWS Cognito with AWS Go SDK in a form
1515

1616
In order this solution to work, you need to have AWS credentials configured (file `.aws/configuration` exists) and User Pool created in AWS Console. You have to disable "Remember device" and enable "Sms second-factor" on authentication tab.
1717

18-
You will also need to create App Client in User Pool without "Generate Secret Key" checkbox. When the app client is created, in it's settings select "Enable username-password (non-SRP) flow for app-based authentication (USER_PASSWORD_AUTH)".
18+
When the app client is created, in it's settings select "Enable username-password (non-SRP) flow for app-based authentication (USER_PASSWORD_AUTH)".
19+
20+
It's possible to use go sdk with client secret. You can read a bit more about generating client secrets here:
21+
https://dev.to/mcharytoniuk/using-aws-cognito-app-client-secret-hash-with-go-8ld
1922

2023
## Build
2124

2225
```go
2326
go build -o ./build/cognito
27+
```
28+
29+
## Run
2430

31+
Without client secret:
32+
33+
```go
2534
AWS_PROFILE=XXX COGNITO_APP_CLIENT_ID=XXX COGNITO_USER_POOL_ID=XXX PORT=8080 ./build/cognito
2635
```
2736

28-
Visit http://localhost:8080/ to see the list of available pages.
37+
With client secret:
38+
39+
```go
40+
AWS_PROFILE=XXX COGNITO_APP_CLIENT_ID=XXX COGNITO_APP_CLIENT_SECRET=XXX COGNITO_USER_POOL_ID=XXX PORT=8080 ./build/cognito
41+
```
42+
43+
It's worth noting that in production environment you should not pass client secrets this way because with adequate permissions it's possible to read environmental variables of a running process. Also if you call a command that way, secret hash will be stored in your shell history. You should keep those issues in mind and mitigate them in your enviroment.
44+
45+
Visit http://localhost:8080/ to see the list of available pages.

app/app.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import (
66

77
// App holds internals for auth flow.
88
type App struct {
9-
CognitoClient *cognito.CognitoIdentityProvider
10-
UserPoolID string
11-
AppClientID string
9+
CognitoClient *cognito.CognitoIdentityProvider
10+
UserPoolID string
11+
AppClientID string
12+
AppClientSecret string
1213
}

app/login.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package app
22

33
import (
4+
// Those imports are required to compute the secret hash.
5+
"crypto/hmac"
6+
"crypto/sha256"
7+
"encoding/base64"
8+
49
"fmt"
510
"net/http"
611

@@ -12,6 +17,17 @@ import (
1217
const flowUsernamePassword = "USER_PASSWORD_AUTH"
1318
const flowRefreshToken = "REFRESH_TOKEN_AUTH"
1419

20+
// Secret hash is not a client secret itself, but a base64 encoded hmac-sha256
21+
// hash.
22+
// The actual AWS documentation on how to compute this hash is here:
23+
// https://docs.aws.amazon.com/cognito/latest/developerguide/signing-up-users-in-your-app.html#cognito-user-pools-computing-secret-hash
24+
func computeSecretHash(clientSecret string, username string, clientId string) string {
25+
mac := hmac.New(sha256.New, []byte(clientSecret))
26+
mac.Write([]byte(username + clientId))
27+
28+
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
29+
}
30+
1531
// Login handles login scenario.
1632
func (a *App) Login(w http.ResponseWriter, r *http.Request) {
1733
r.ParseForm()
@@ -27,6 +43,13 @@ func (a *App) Login(w http.ResponseWriter, r *http.Request) {
2743
"PASSWORD": aws.String(password),
2844
}
2945

46+
// Compute secret hash based on client secret.
47+
if a.AppClientSecret != "" {
48+
secretHash := computeSecretHash(a.AppClientSecret, username, a.AppClientID)
49+
50+
params["SECRET_HASH"] = aws.String(secretHash)
51+
}
52+
3053
if refresh != "" {
3154
flow = aws.String(flowRefreshToken)
3255
params = map[string]*string{

main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ func main() {
5959
}
6060

6161
example := app.App{
62-
CognitoClient: cognito.New(sess),
63-
UserPoolID: os.Getenv("COGNITO_USER_POOL_ID"),
64-
AppClientID: os.Getenv("COGNITO_APP_CLIENT_ID"),
62+
CognitoClient: cognito.New(sess),
63+
UserPoolID: os.Getenv("COGNITO_USER_POOL_ID"),
64+
AppClientID: os.Getenv("COGNITO_APP_CLIENT_ID"),
65+
AppClientSecret: os.Getenv("COGNITO_APP_CLIENT_SECRET"),
6566
}
6667

6768
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)