-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[core-amqp][event-hubs] add support for NamedKeyCredential and SASCredential #14423
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
33615a1
[event-hubs] add support for NamedKeyCredential and SASCredential
chradek 2490df3
[core-amqp] add TokenProviders for handling NamedKeyCredential and SA…
chradek 2951cbc
[event-hubs] use TokenProviders from core-amqp and add more tests
chradek ce57aa3
missed a formatting thing
chradek 017353c
[core-amqp] fix doc comment
chradek 6f69930
[core-amqp] merge NamedKeyTokenProvider and SharedAccessSignatureToke…
chradek d85588d
[event-hubs] update to use SasTokenProvider
chradek 7841000
[core-amqp] move isNamedKeyCredential and isSASCredential typeguards …
chradek 30202e8
[event-hubs] update TokenProvider references to SasTokenProvider
chradek 3e69935
[core-auth] update changelog
chradek 067fcca
[core-amqp] internalize SasTokenProviderImpl and remove isCredential
chradek c3633d5
[event-hubs] update after changes to core-amqp
chradek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import { | ||
AccessToken, | ||
NamedKeyCredential, | ||
SASCredential, | ||
isNamedKeyCredential, | ||
isSASCredential | ||
} from "@azure/core-auth"; | ||
import jssha from "jssha"; | ||
import { isObjectWithProperties } from "../util/typeGuards"; | ||
|
||
/** | ||
* A SasTokenProvider provides an alternative to TokenCredential for providing an `AccessToken`. | ||
* @hidden | ||
*/ | ||
export interface SasTokenProvider { | ||
/** | ||
* Property used to distinguish SasTokenProvider from TokenCredential. | ||
*/ | ||
isSasTokenProvider: true; | ||
/** | ||
* Gets the token provided by this provider. | ||
* | ||
* This method is called automatically by Azure SDK client libraries. | ||
* | ||
* @param audience - The audience for which the token is desired. | ||
*/ | ||
getToken(audience: string): AccessToken; | ||
} | ||
|
||
/** | ||
* Creates a token provider from the provided shared access data. | ||
* @param data - The sharedAccessKeyName/sharedAccessKey pair or the sharedAccessSignature. | ||
* @hidden | ||
*/ | ||
export function createSasTokenProvider( | ||
data: | ||
| { sharedAccessKeyName: string; sharedAccessKey: string } | ||
| { sharedAccessSignature: string } | ||
| NamedKeyCredential | ||
| SASCredential | ||
): SasTokenProvider { | ||
if (isNamedKeyCredential(data) || isSASCredential(data)) { | ||
return new SasTokenProviderImpl(data); | ||
} else if (isObjectWithProperties(data, ["sharedAccessKeyName", "sharedAccessKey"])) { | ||
return new SasTokenProviderImpl({ name: data.sharedAccessKeyName, key: data.sharedAccessKey }); | ||
} else { | ||
return new SasTokenProviderImpl({ signature: data.sharedAccessSignature }); | ||
} | ||
} | ||
|
||
/** | ||
* A TokenProvider that generates a Sas token: | ||
* `SharedAccessSignature sr=<resource>&sig=<signature>&se=<expiry>&skn=<keyname>` | ||
* | ||
* @internal | ||
*/ | ||
export class SasTokenProviderImpl implements SasTokenProvider { | ||
/** | ||
* Property used to distinguish TokenProvider from TokenCredential. | ||
*/ | ||
get isSasTokenProvider(): true { | ||
return true; | ||
} | ||
|
||
/** | ||
* The SASCredential containing the key name and secret key value. | ||
*/ | ||
private _credential: SASCredential | NamedKeyCredential; | ||
|
||
/** | ||
* Initializes a new instance of SasTokenProvider | ||
* @param credential - The source `NamedKeyCredential` or `SASCredential`. | ||
*/ | ||
constructor(credential: SASCredential | NamedKeyCredential) { | ||
this._credential = credential; | ||
} | ||
|
||
/** | ||
* Gets the sas token for the specified audience | ||
* @param audience - The audience for which the token is desired. | ||
*/ | ||
getToken(audience: string): AccessToken { | ||
if (isNamedKeyCredential(this._credential)) { | ||
return createToken( | ||
this._credential.name, | ||
this._credential.key, | ||
Math.floor(Date.now() / 1000) + 3600, | ||
audience | ||
); | ||
} else { | ||
return { | ||
token: this._credential.signature, | ||
expiresOnTimestamp: 0 | ||
}; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Creates the sas token based on the provided information. | ||
* @param keyName - The shared access key name. | ||
* @param key - The shared access key. | ||
* @param expiry - The time period in unix time after which the token will expire. | ||
* @param audience - The audience for which the token is desired. | ||
* @internal | ||
*/ | ||
function createToken(keyName: string, key: string, expiry: number, audience: string): AccessToken { | ||
audience = encodeURIComponent(audience); | ||
keyName = encodeURIComponent(keyName); | ||
const stringToSign = audience + "\n" + expiry; | ||
|
||
const shaObj = new jssha("SHA-256", "TEXT"); | ||
shaObj.setHMACKey(key, "TEXT"); | ||
shaObj.update(stringToSign); | ||
const sig = encodeURIComponent(shaObj.getHMAC("B64")); | ||
return { | ||
token: `SharedAccessSignature sr=${audience}&sig=${sig}&se=${expiry}&skn=${keyName}`, | ||
expiresOnTimestamp: expiry | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import { SasTokenProvider, createSasTokenProvider } from "./auth/tokenProvider"; | ||
import { isSasTokenProvider } from "./util/typeGuards"; | ||
|
||
export { SasTokenProvider, createSasTokenProvider, isSasTokenProvider }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import chai from "chai"; | ||
const should = chai.should(); | ||
import { AzureNamedKeyCredential, AzureSASCredential } from "@azure/core-auth"; | ||
import { createSasTokenProvider } from "../src/index"; | ||
|
||
describe("SasTokenProvider", function(): void { | ||
describe("createSasTokenProvider", () => { | ||
it("should work as expected with AzureNamedKeyCredential", async function(): Promise<void> { | ||
const keyName = "myKeyName"; | ||
const key = "importantValue"; | ||
const tokenProvider = createSasTokenProvider(new AzureNamedKeyCredential(keyName, key)); | ||
const now = Math.floor(Date.now() / 1000) + 3600; | ||
const tokenInfo = tokenProvider.getToken("myaudience"); | ||
tokenInfo.token.should.match( | ||
/SharedAccessSignature sr=myaudience&sig=(.*)&se=\d{10}&skn=myKeyName/g | ||
); | ||
tokenInfo.expiresOnTimestamp.should.equal(now); | ||
}); | ||
|
||
it("should work as expected with `shareAccessKeyName` and `sharedAccessKey`", async function(): Promise< | ||
void | ||
> { | ||
// This is how createSasTokenProvider will be called if SAK params are passed through a connection string. | ||
const tokenProvider = createSasTokenProvider({ | ||
sharedAccessKeyName: "sakName", | ||
sharedAccessKey: "sak" | ||
}); | ||
const now = Math.floor(Date.now() / 1000) + 3600; | ||
const tokenInfo = tokenProvider.getToken("sb://hostname.servicebus.windows.net/"); | ||
tokenInfo.token.should.match( | ||
/SharedAccessSignature sr=sb%3A%2F%2Fhostname.servicebus.windows.net%2F&sig=(.*)&se=\d{10}&skn=sakName/g | ||
); | ||
tokenInfo.expiresOnTimestamp.should.equal(now); | ||
}); | ||
}); | ||
|
||
it("should work as expected with AzureSASCredential", async function(): Promise<void> { | ||
const sasTokenProvider = createSasTokenProvider( | ||
new AzureSASCredential("SharedAccessSignature se=<blah>") | ||
); | ||
const accessToken = sasTokenProvider.getToken("audience isn't used"); | ||
|
||
should.equal( | ||
accessToken.token, | ||
"SharedAccessSignature se=<blah>", | ||
"SAS URI we were constructed with should just be returned verbatim without interpretation (and the audience is ignored)" | ||
); | ||
|
||
should.equal( | ||
accessToken.expiresOnTimestamp, | ||
0, | ||
"SAS URI always returns 0 for expiry (ignoring what's in the SAS token)" | ||
); | ||
}); | ||
|
||
it("should work as expected with `sharedAccessSignature`", async function(): Promise<void> { | ||
// This is how createSasTokenProvider will be called if the shared access signature is passed through a connection string. | ||
const tokenProvider = createSasTokenProvider({ sharedAccessSignature: "<blah>" }); | ||
const tokenInfo = tokenProvider.getToken("sb://hostname.servicebus.windows.net/"); | ||
tokenInfo.token.should.match(/<blah>/g); | ||
tokenInfo.expiresOnTimestamp.should.equal(0); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really wanted to do
export * as Internals from "./internals";
but unfortunately api-extractor doesn't support it (yet).microsoft/rushstack#2412