Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more cases to k6 #4261

Merged
merged 12 commits into from
Jun 3, 2024
Merged
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ pkg/lib/webparcel/parcel_gen.go

# OSX
.DS_Store

# custom build of k6
/k6/k6
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ vendor:
go install github.com/google/wire/cmd/wire
go install golang.org/x/vuln/cmd/govulncheck@latest
go install golang.org/x/tools/cmd/goimports@latest
go install go.k6.io/xk6/cmd/xk6@latest
npm --prefix ./scripts/npm ci
npm --prefix ./authui ci
npm --prefix ./portal ci
Expand All @@ -37,6 +38,7 @@ generate:

.PHONY: test
test:
$(MAKE) -C ./k6 go-test
go test ./pkg/... -timeout 1m30s

.PHONY: lint
Expand Down
34 changes: 28 additions & 6 deletions k6/Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#export K6_HTTP_DEBUG=true
# We cannot use K6_DURATION and K6_VUS because
# using them will make k6 to ignore scenarios.
export DURATION=10s
export VUS=10
export ENDPOINT=http://localhost:3100
export ENDPOINT=https://loadtest.authgear-loadtest.com
export CLIENT_ID=test
export REDIRECT_URI=com.example://host
export FIXTURE_FIXED_OTP=000000
Expand All @@ -13,14 +11,38 @@ export FIXTURE_PHONE_NUMBER_COUNTRY_CALLING_CODE=+852
export FIXTURE_PHONE_NUMBER_LOW=52000000
export FIXTURE_PHONE_NUMBER_HIGH=57999999

.PHONY: go-test
go-test:
go test .

.PHONY: k6
k6:
xk6 build --with github.com/authgear/authgear-server/k6=.

.PHONY: clean
clean:
rm -rf ./k6

.PHONY: signup
signup:
k6 run --vus $(VUS) --duration $(DURATION) ./signup.js
./k6 run --config options.json ./signup.js

.PHONY: login
login:
k6 run --vus $(VUS) --duration $(DURATION) ./login.js
./k6 run --config options.json ./login.js

.PHONY: refresh_access_token
refresh_access_token:
./k6 run --config options.json ./refresh_access_token.js

.PHONY: get_user_info
get_user_info:
./k6 run --config options.json ./get_user_info.js

.PHONY: biometric_login
biometric_login:
./k6 run --config options.json ./biometric_login.js

.PHONY: healthz
healthz:
k6 run --vus $(VUS) --duration $(DURATION) ./healthz.js
./k6 run --config options.json ./healthz.js
22 changes: 16 additions & 6 deletions k6/authflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function authflowCreate({ type, name, questionMarkQuery }) {
const json = response.json();
const checkResult = checkJSON(json);
if (!checkResult) {
fail(`failed to create authflow ${type}:${name}`);
fail(`failed to create authflow ${type}:${name} ${JSON.stringify(json)}`);
}
return {
response,
Expand All @@ -66,7 +66,7 @@ export function authflowInput({ result, input }) {
const json = response.json();
const checkResult = checkJSON(json);
if (!checkResult) {
fail(`failed to input: ${state_token}`);
fail(`failed to input: ${state_token} ${JSON.stringify(json)}`);
}
return {
response,
Expand All @@ -82,12 +82,12 @@ export function redirect(result) {
typeof r.action.data.finish_redirect_uri === "string",
});
if (!checkResult) {
fail(`unexpected action: ${result.action}`);
fail(`unexpected action: ${result}`);
}
return http.get(result.action.data.finish_redirect_uri);
}

function authflowIdentify({ phone, email, result }) {
function authflowIdentify({ username, phone, email, result }) {
const options = result.action.data.options;
const first = options[0];
switch (first.identification) {
Expand All @@ -107,6 +107,14 @@ function authflowIdentify({ phone, email, result }) {
login_id: phone,
},
});
case "username":
return authflowInput({
result,
input: {
identification: "username",
login_id: username,
},
});
default:
throw new Error("unexpected identification: ", first.identification);
}
Expand Down Expand Up @@ -223,7 +231,7 @@ function authflowAuthenticate({ result }) {
}
}

export function authflowRun({ phone, email, type, name }) {
export function authflowRun({ username, phone, email, type, name }) {
const url = makeAuthenticationURL({
endpoint: ENDPOINT,
client_id: CLIENT_ID,
Expand All @@ -243,7 +251,7 @@ export function authflowRun({ phone, email, type, name }) {
while (ret.result.action.type !== "finished") {
switch (ret.result.action.type) {
case "identify":
ret = authflowIdentify({ phone, email, result: ret.result });
ret = authflowIdentify({ username, phone, email, result: ret.result });
break;
case "verify":
ret = authflowVerify({ result: ret.result });
Expand Down Expand Up @@ -274,4 +282,6 @@ export function authflowRun({ phone, email, type, name }) {
if (!checkResult) {
fail("failed to exchange code");
}

return tokenResponse;
}
2 changes: 2 additions & 0 deletions k6/authgear.features.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rate_limits:
disabled: true
135 changes: 135 additions & 0 deletions k6/authgear.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package authgear

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"strings"

"github.com/google/uuid"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jws"
"go.k6.io/k6/js/modules"
)

func init() {
modules.Register("k6/x/authgear", new(Authgear))
}

func toJavaScript(v interface{}) interface{} {
b, err := json.Marshal(v)
if err != nil {
panic(err)
}
var out interface{}
err = json.Unmarshal(b, &out)
if err != nil {
panic(err)
}
return out
}

func toJSONBytes(v interface{}) []byte {
b, err := json.Marshal(v)
if err != nil {
panic(err)
}
return b
}

type Authgear struct{}

func (*Authgear) Uuid() string {
return uuid.Must(uuid.NewRandom()).String()
}

func (*Authgear) GenerateRSAPrivateKeyInPKCS8PEM(bits int) string {
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
panic(err)
}

der, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
panic(err)
}

block := &pem.Block{
Type: "PRIVATE KEY",
Bytes: der,
}
var buf strings.Builder
err = pem.Encode(&buf, block)
if err != nil {
panic(err)
}

return buf.String()
}

func (*Authgear) JwkKeyFromPKCS8PEM(pkcs8Pem string) interface{} {
key, err := jwk.ParseKey([]byte(pkcs8Pem), jwk.WithPEM(true))
if err != nil {
panic(err)
}

return toJavaScript(key)
}

func (*Authgear) JwkPublicKeyFromJWKPrivateKey(jwkValue interface{}) interface{} {
jwkBytes := toJSONBytes(jwkValue)
jwkPrivateKey, err := jwk.ParseKey(jwkBytes)
if err != nil {
panic(err)
}
jwkPublicKey, err := jwkPrivateKey.PublicKey()
if err != nil {
panic(err)
}

return toJavaScript(jwkPublicKey)
}

func (*Authgear) SignJWT(alg string, jwkValue interface{}, payload interface{}, headers map[string]interface{}) string {
jwkBytes := toJSONBytes(jwkValue)
jwkPrivateKey, err := jwk.ParseKey(jwkBytes)
if err != nil {
panic(err)
}

payloadBytes := toJSONBytes(payload)

hdr := jws.NewHeaders()
for k, v := range headers {
switch k {
case "jwk":
jwkKey, err := jwk.ParseKey(toJSONBytes(v))
if err != nil {
panic(err)
}
err = hdr.Set(k, jwkKey)
default:
err = hdr.Set(k, v)
}
if err != nil {
panic(err)
}
}

b, err := jws.Sign(
payloadBytes,
jws.WithKey(
jwa.SignatureAlgorithm(alg),
jwkPrivateKey,
jws.WithProtectedHeaders(hdr),
),
)
if err != nil {
panic(err)
}

return string(b)
}
62 changes: 62 additions & 0 deletions k6/authgear.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
authentication:
identities:
- login_id
primary_authenticators:
- password
secondary_authentication_mode: disabled
authenticator:
oob_otp:
sms:
phone_otp_mode: sms
http:
public_origin: http://localhost:3100
id: loadtest
identity:
login_id:
keys:
- type: email
localization:
fallback_language: en
supported_languages:
- en
oauth:
clients:
- access_token_lifetime_seconds: 300
client_id: test
client_name: loadtest
grant_types:
- authorization_code
- refresh_token
issue_jwt_access_token: true
name: loadtest
redirect_uris:
- "com.example://host"
response_types:
- none
- code
test_mode:
oob_otp:
enabled: true
rules:
- fixed_code: "000000"
regex: .+
sms:
enabled: true
rules:
- regex: .+
suppressed: true
email:
enabled: true
rules:
- regex: .+
suppressed: true
ui:
implementation: authflowv2
verification:
claims:
email:
enabled: true
required: false
phone_number:
enabled: true
required: false
Loading
Loading