Skip to content

Commit 5d5a56a

Browse files
naomi-lgbtlukeocodes
authored andcommitted
fix: make temporary auth tokens actually work
1 parent 7b22b07 commit 5d5a56a

6 files changed

Lines changed: 110 additions & 14 deletions

File tree

examples/node-auth/index.js

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
const { createClient } = require("../../dist/main/index");
1+
const { createClient, LiveTranscriptionEvents } = require("../../dist/main/index");
2+
const fetch = require("cross-fetch");
23

3-
const transcribeUrl = async () => {
4-
const deepgram = createClient(process.env.DEEPGRAM_API_KEY);
4+
const demonstrateAuth = async () => {
5+
// Note that the ENV value here is different than our other examples. The SDK will automatically grab `DEEPGRAM_API_KEY` if it exists,
6+
// so by using a different name we can ensure that the second client is properly consuming the authentication token instead.
7+
const deepgram = createClient({ key: process.env.DIFFERENT_DEEPGRAM_API_KEY });
58
const { result: token, error: tokenError } = await deepgram.auth.grantToken();
69
if (tokenError) {
710
throw tokenError;
811
}
9-
console.log("Token", token);
12+
console.log("Token", token.access_token);
1013

1114
console.log("Transcribing URL", "https://dpgr.am/spacewalk.wav");
12-
deepgram.token = token.access_token;
13-
const { result, error } = await deepgram.listen.prerecorded.transcribeUrl(
15+
const deepgramWithToken = createClient({ accessToken: token.access_token });
16+
const { result, error } = await deepgramWithToken.listen.prerecorded.transcribeUrl(
1417
{
1518
url: "https://dpgr.am/spacewalk.wav",
1619
},
@@ -22,6 +25,86 @@ const transcribeUrl = async () => {
2225

2326
if (error) console.error(error);
2427
if (!error) console.dir(result, { depth: 5 });
28+
29+
console.log("Transcribing live");
30+
31+
const url = "http://stream.live.vc.bbcmedia.co.uk/bbc_world_service";
32+
33+
let is_finals = [];
34+
35+
const connection = deepgramWithToken.listen.live({
36+
model: "nova-3",
37+
language: "en-US",
38+
// Apply smart formatting to the output
39+
smart_format: true,
40+
// To get UtteranceEnd, the following must be set:
41+
interim_results: true,
42+
utterance_end_ms: 1000,
43+
vad_events: true,
44+
// Time in milliseconds of silence to wait for before finalizing speech
45+
endpointing: 300,
46+
// Keyterm Prompting allows you improve Keyword Recall Rate (KRR) for important keyterms or phrases up to 90%.
47+
keyterm: ["BBC"],
48+
});
49+
50+
connection.on(LiveTranscriptionEvents.Open, () => {
51+
fetch(url)
52+
.then((r) => r.body)
53+
.then((res) => {
54+
res.on("readable", () => {
55+
connection.send(res.read());
56+
});
57+
});
58+
});
59+
connection.on(LiveTranscriptionEvents.Close, () => {
60+
console.log("Connection closed.");
61+
});
62+
63+
connection.on(LiveTranscriptionEvents.Metadata, (data) => {
64+
console.log(`Deepgram Metadata: ${data}`);
65+
});
66+
67+
connection.on(LiveTranscriptionEvents.Transcript, (data) => {
68+
const sentence = data.channel.alternatives[0].transcript;
69+
70+
// Ignore empty transcripts
71+
if (sentence.length == 0) {
72+
return;
73+
}
74+
if (data.is_final) {
75+
// We need to collect these and concatenate them together when we get a speech_final=true
76+
// See docs: https://developers.deepgram.com/docs/understand-endpointing-interim-results
77+
is_finals.push(sentence);
78+
79+
// Speech final means we have detected sufficent silence to consider this end of speech
80+
// Speech final is the lowest latency result as it triggers as soon an the endpointing value has triggered
81+
if (data.speech_final) {
82+
const utterance = is_finals.join(" ");
83+
console.log(`Speech Final: ${utterance}`);
84+
is_finals = [];
85+
} else {
86+
// These are useful if you need real time captioning and update what the Interim Results produced
87+
console.log(`Is Final: ${sentence}`);
88+
}
89+
} else {
90+
// These are useful if you need real time captioning of what is being spoken
91+
console.log(`Interim Results: ${sentence}`);
92+
}
93+
});
94+
95+
connection.on(LiveTranscriptionEvents.UtteranceEnd, (_data) => {
96+
const utterance = is_finals.join(" ");
97+
console.log(`Deepgram UtteranceEnd: ${utterance}`);
98+
is_finals = [];
99+
});
100+
101+
connection.on(LiveTranscriptionEvents.SpeechStarted, (_data) => {
102+
// console.log("Deepgram SpeechStarted");
103+
});
104+
105+
connection.on(LiveTranscriptionEvents.Error, (err) => {
106+
console.error(err);
107+
});
25108
};
26109

27-
transcribeUrl();
110+
demonstrateAuth();

src/lib/fetch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ export const resolveFetch = (customFetch?: Fetch): Fetch => {
2929
* @param customFetch - An optional custom fetch function to use instead of the global fetch function.
3030
* @returns A fetch function that can be used to make HTTP requests with the provided API key in the "Authorization" header.
3131
*/
32-
export const fetchWithAuth = (apiKey: string, customFetch?: Fetch): Fetch => {
32+
export const fetchWithAuth = (apiKey: string, customFetch?: Fetch, token?: string): Fetch => {
3333
const fetch = resolveFetch(customFetch);
3434
const HeadersConstructor = resolveHeadersConstructor();
3535

3636
return async (input, init) => {
3737
const headers = new HeadersConstructor(init?.headers);
3838

3939
if (!headers.has("Authorization")) {
40-
headers.set("Authorization", `Token ${apiKey}`);
40+
headers.set("Authorization", token ? `Bearer ${token}` : `Token ${apiKey}`);
4141
}
4242

4343
return fetch(input, { ...init, headers });

src/lib/types/DeepgramClientOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export type DefaultClientOptions = {
5858
*/
5959
export interface DeepgramClientOptions {
6060
key?: string | IKeyFactory;
61+
accessToken?: string;
6162
global?: NamespaceOptions & { url?: string; headers?: { [index: string]: any } };
6263
listen?: NamespaceOptions;
6364
manage?: NamespaceOptions;

src/packages/AbstractClient.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const noop = () => {};
2424
export abstract class AbstractClient extends EventEmitter {
2525
protected factory: Function | undefined = undefined;
2626
protected key: string;
27+
protected accessToken: string | undefined = undefined;
2728
protected options: DefaultClientOptions;
2829
public namespace: string = "global";
2930
public version: string = "v1";
@@ -43,6 +44,10 @@ export abstract class AbstractClient extends EventEmitter {
4344
constructor(options: DeepgramClientOptions) {
4445
super();
4546

47+
if (options.accessToken) {
48+
this.accessToken = options.accessToken;
49+
}
50+
4651
let key;
4752

4853
if (typeof options.key === "function") {
@@ -56,8 +61,8 @@ export abstract class AbstractClient extends EventEmitter {
5661
key = process.env.DEEPGRAM_API_KEY as string;
5762
}
5863

59-
if (!key) {
60-
throw new DeepgramError("A deepgram API key is required.");
64+
if (!key && !this.accessToken) {
65+
throw new DeepgramError("A deepgram API key or temporary auth token is required.");
6166
}
6267

6368
this.key = key;

src/packages/AbstractLiveClient.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ export abstract class AbstractLiveClient extends AbstractClient {
9090
}
9191

9292
if (!("Authorization" in this.headers)) {
93-
this.headers["Authorization"] = `Token ${key}`; // Add default token
93+
if (this.accessToken) {
94+
this.headers["Authorization"] = `Bearer ${this.accessToken}`; // Use token if available
95+
} else {
96+
this.headers["Authorization"] = `Token ${key}`; // Add default token
97+
}
9498
}
9599
}
96100

@@ -141,7 +145,10 @@ export abstract class AbstractLiveClient extends AbstractClient {
141145
* Native websocket transport (browser)
142146
*/
143147
if (NATIVE_WEBSOCKET_AVAILABLE) {
144-
this.conn = new WebSocket(requestUrl, ["token", this.namespaceOptions.key]);
148+
this.conn = new WebSocket(
149+
requestUrl,
150+
this.accessToken ? ["bearer", this.accessToken] : ["token", this.namespaceOptions.key]
151+
);
145152
this.setupConnection();
146153
return;
147154
}

src/packages/AbstractRestClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export abstract class AbstractRestClient extends AbstractClient {
2929
);
3030
}
3131

32-
this.fetch = fetchWithAuth(this.key, this.namespaceOptions.fetch.client);
32+
this.fetch = fetchWithAuth(this.key, this.namespaceOptions.fetch.client, this.accessToken);
3333

3434
if (this.proxy) {
3535
this.baseUrl = this.namespaceOptions.fetch.options.proxy!.url;

0 commit comments

Comments
 (0)