Skip to content

Commit

Permalink
JWT-auth: Support aud as string array (envoyproxy#994)
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue.

JWT-auth: Support aud as string array

**What this PR does / why we need it**:

To support "aud" in JWT as string array.

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

**Special notes for your reviewer**:

**Release note**:

```release-note
None
```
  • Loading branch information
qiwzhang authored and Istio Auto Merge Robot committed Feb 6, 2018
1 parent 862c53a commit 4a59a04
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 13 deletions.
32 changes: 23 additions & 9 deletions src/envoy/auth/jwt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ Jwt::Jwt(const std::string &jwt) {
header_str_ = Base64UrlDecode(header_str_base64url_);
try {
header_ = Json::Factory::loadFromString(header_str_);
} catch (...) {
} catch (Json::Exception &e) {
UpdateStatus(Status::JWT_HEADER_PARSE_ERROR);
return;
}
Expand All @@ -241,7 +241,7 @@ Jwt::Jwt(const std::string &jwt) {
}
try {
alg_ = header_->getString("alg");
} catch (...) {
} catch (Json::Exception &e) {
UpdateStatus(Status::JWT_HEADER_BAD_ALG);
return;
}
Expand All @@ -260,7 +260,7 @@ Jwt::Jwt(const std::string &jwt) {
// Header may contain "kid", which should be a string if exists.
try {
kid_ = header_->getString("kid", "");
} catch (...) {
} catch (Json::Exception &e) {
UpdateStatus(Status::JWT_HEADER_BAD_KID);
return;
}
Expand All @@ -271,16 +271,30 @@ Jwt::Jwt(const std::string &jwt) {
payload_str_ = Base64UrlDecode(payload_str_base64url_);
try {
payload_ = Json::Factory::loadFromString(payload_str_);
} catch (...) {
} catch (Json::Exception &e) {
UpdateStatus(Status::JWT_PAYLOAD_PARSE_ERROR);
return;
}

iss_ = payload_->getString("iss", "");
aud_ = payload_->getString("aud", "");
sub_ = payload_->getString("sub", "");
exp_ = payload_->getInteger("exp", 0);

// "aud" can be either string array or string.
// Try as string array, read it as empty array if doesn't exist.
try {
aud_ = payload_->getStringArray("aud", true);
} catch (Json::Exception &e) {
// Try as string
try {
auto audience = payload_->getString("aud");
aud_.push_back(audience);
} catch (Json::Exception &e) {
UpdateStatus(Status::JWT_PAYLOAD_PARSE_ERROR);
return;
}
}

// Set up signature
signature_ =
Base64UrlDecode(std::string(jwt_split[2].begin(), jwt_split[2].end()));
Expand Down Expand Up @@ -369,7 +383,7 @@ Json::ObjectSharedPtr Jwt::Payload() { return payload_; }
const std::string &Jwt::PayloadStr() { return payload_str_; }
const std::string &Jwt::PayloadStrBase64Url() { return payload_str_base64url_; }
const std::string &Jwt::Iss() { return iss_; }
const std::string &Jwt::Aud() { return aud_; }
const std::vector<std::string> &Jwt::Aud() { return aud_; }
const std::string &Jwt::Sub() { return sub_; }
int64_t Jwt::Exp() { return exp_; }

Expand All @@ -390,7 +404,7 @@ void Pubkeys::CreateFromJwksCore(const std::string &pkey_jwks) {
Json::ObjectSharedPtr jwks_json;
try {
jwks_json = Json::Factory::loadFromString(pkey_jwks);
} catch (...) {
} catch (Json::Exception &e) {
UpdateStatus(Status::JWK_PARSE_ERROR);
return;
}
Expand All @@ -401,7 +415,7 @@ void Pubkeys::CreateFromJwksCore(const std::string &pkey_jwks) {
}
try {
keys = jwks_json->getObjectArray("keys", true);
} catch (...) {
} catch (Json::Exception &e) {
UpdateStatus(Status::JWK_BAD_KEYS);
return;
}
Expand All @@ -416,7 +430,7 @@ void Pubkeys::CreateFromJwksCore(const std::string &pkey_jwks) {
pubkey->alg_specified_ = true;
n_str = jwk_json->getString("n");
e_str = jwk_json->getString("e");
} catch (...) {
} catch (Json::Exception &e) {
continue;
}
EvpPkeyGetter e;
Expand Down
4 changes: 2 additions & 2 deletions src/envoy/auth/jwt.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class Jwt : public WithStatus {

// It returns the "aud" claim value of the given JWT, or an empty string if
// "aud" claim does not exist.
const std::string& Aud();
const std::vector<std::string>& Aud();

// It returns the "sub" claim value of the given JWT, or an empty string if
// "sub" claim does not exist.
Expand All @@ -232,7 +232,7 @@ class Jwt : public WithStatus {
std::string alg_;
std::string kid_;
std::string iss_;
std::string aud_;
std::vector<std::string> aud_;
std::string sub_;
int64_t exp_;

Expand Down
14 changes: 14 additions & 0 deletions src/envoy/auth/jwt_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ class DatasetPem {
"N09hdvlCtAF87Fu1qqfwEQ93A-J7m08bZJoyIPcNmTcYGHwfMR4-lcI5cC_93C_"
"5BGE1FHPLOHpNghLuM6-rhOtgwZc9ywupn_bBK3QzuAoDnYwpqQhgQL_CdUD_bSHcmWFkw";

// {"iss":"https://example.com","sub":"test@example.com","exp":1501281058,
// aud: [aud1, aud2] }
// signature part is invalid.
const std::string kJwtMultiSub =
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFmMDZjMTlmOGU1YjMzMTUyMT"
"ZkZjAxMGZkMmI5YTkzYmFjMTM1YzgifQ.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tI"
"iwiaWF0IjoxNTE3ODc1MDU5LCJhdWQiOlsiYXVkMSIsImF1ZDIiXSwiZXhwIjoxNTE3ODc"
"4NjU5LCJzdWIiOiJodHRwczovL2V4YW1wbGUuY29tIn0.fzzlfQG2wZpPRRAPa6Yu";

const std::string kJwtSub = "test@example.com";
const std::string kJwtHeaderEncoded = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";
const std::string kJwtPayloadEncoded =
Expand Down Expand Up @@ -378,6 +387,11 @@ TEST_F(JwtTestPem, OK) {
DoTest(ds.kJwt, ds.kPublicKey, "pem", true, Status::OK, payload);
}

TEST_F(JwtTestPem, MultiAudiences) {
Jwt jwt(ds.kJwtMultiSub);
ASSERT_EQ(jwt.Aud(), std::vector<std::string>({"aud1", "aud2"}));
}

TEST_F(JwtTestPem, InvalidSignature) {
auto invalid_jwt = ds.kJwt;
invalid_jwt[ds.kJwt.length() - 2] =
Expand Down
12 changes: 10 additions & 2 deletions src/envoy/auth/pubkey_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,16 @@ class PubkeyCacheItem {
const Pubkeys* pubkey() const { return pubkey_.get(); }

// Check if an audience is allowed.
bool IsAudienceAllowed(const std::string& aud) {
return audiences_.empty() || audiences_.find(aud) != audiences_.end();
bool IsAudienceAllowed(const std::vector<std::string>& jwt_audiences) {
if (audiences_.empty()) {
return true;
}
for (const auto& aud : jwt_audiences) {
if (audiences_.find(aud) != audiences_.end()) {
return true;
}
}
return false;
}

// Set a pubkey as string.
Expand Down

0 comments on commit 4a59a04

Please sign in to comment.