Skip to content

Commit 00b2ec4

Browse files
GermanBluefoxApollon77Copilot
authored
Oauth2 (#613)
* Added the token refresher class * Added the token refresher class * Fixed linter * Added alias for translate => t * Rework on TokenRefresher * Update src/TokenRefresher.ts Co-authored-by: Ingo Fischer <github@fischer-ka.de> * Update src/TokenRefresher.ts Co-authored-by: Ingo Fischer <github@fischer-ka.de> * Small changes on translate function: allow replacement of %s arguments even if no translation exist * Update src/TokenRefresher.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Formatting --------- Co-authored-by: Ingo Fischer <github@fischer-ka.de> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent fb8889a commit 00b2ec4

27 files changed

+2942
-1828
lines changed

README.md

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Note that `commonTools.letsEncrypt` is not available anymore as the next control
8181
8282
## I18n
8383
84-
Developer can use internationalisation in backend.
84+
Developers can use internationalisation in backend.
8585
8686
For that call
8787
@@ -100,6 +100,10 @@ and then in code
100100
101101
```javascript
102102
console.log(I18n.translate('text to translate %s', 'argument1'));
103+
104+
// Or you can use short form
105+
console.log(I18n.t('text to translate %s', 'argument1'));
106+
103107
// or to get the ioBroker.Translated object
104108
console.log(JSON.stringify(I18n.getTranslatedObject('text to translate %s and %s', 'argument1', 'argument2')));
105109
```
@@ -148,6 +152,49 @@ ioBroker has the ability to include files written by adapters in its backups. To
148152
149153
This path is relative to the path returned by `getAbsoluteDefaultDataDir()`. The placeholder `%INSTANCE%` is automatically replaced by the instance number of each adapter, for example `"dataFolder": "my-adapter.%INSTANCE%"`.
150154
155+
## OAuth2 token refresher
156+
To keep the OAuth2 access token up to date, you can use the `TokenRefresher` class and do not forget to include `axios` in your adapter dependencies (in `package.json`).
157+
158+
```Typescript
159+
import { TokenRefresher } from '@iobroker/adapter-core';
160+
161+
export class YourAdapter extends Adapter {
162+
private tokenWorker?: TokenRefresher;
163+
164+
// It is important to call this method in the `onReady` method of your adapter and not in the constructor.
165+
async onReady(): Promise<void> {
166+
// Not in constructor, but in onReady
167+
this.tokenWorker = new TokenRefresher(this, 'yourService');
168+
// Your other initialization code...
169+
170+
// Then later in code, you can get the access token like this:
171+
try {
172+
const accessToken = await this.tokenWorker.getAccessToken();
173+
this.log.info(`Spotify OAuth2 Token Refresher is ready: ${accessToken}`);
174+
} catch (error) {
175+
this.log.error(`Error initializing Spotify OAuth2 Token Refresher: ${error}`);
176+
}
177+
}
178+
179+
onStateChange(id: string, state: ioBroker.State | null | undefined): void {
180+
// It detects `adapterName.X.oauth2Tokens` or similar
181+
this.tokenWorker?.onStateChange(id, state);
182+
// Your other state change code...
183+
}
184+
185+
onUnload(callback: () => void): void {
186+
this.tokenWorker?.destroy();
187+
// Your other unload code...
188+
callback();
189+
}
190+
}
191+
```
192+
193+
Important to use OAuth2 infrastructure in your adapter [contact](mailto:info@iobroker.net) the ioBroker team so we can implement a cloud code for https://oauth2.iobroker.in/YOUR_SERVICE_NAME.
194+
This will allow you to use the OAuth2 infrastructure in your adapter without having to implement it yourself.
195+
196+
Here is a description of how to implement OAuth2 in your adapter: https://github.com/ioBroker/ioBroker.admin/blob/master/packages/jsonConfig/OAUTH2.md
197+
151198
## Tips while working on this module
152199
153200
- `npm run build` creates a clean rebuild of the module. This is done automatically before every build;
@@ -164,6 +211,10 @@ If you find errors in the definitions, e.g., function calls that should be allow
164211
Placeholder for the next version (at the beginning of the line):
165212
### **WORK IN PROGRESS**
166213
-->
214+
### **WORK IN PROGRESS**
215+
216+
- (@GermanBluefox) Added the token refresher class
217+
167218
### 3.2.3 (2024-12-04)
168219

169220
- (@foxriver76) loosen `@iobroker/types` peer dependency

build/cjs/TokenRefresher.d.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Token structure
3+
*/
4+
export interface AccessTokens {
5+
/** The access token used for authentication */
6+
access_token: string;
7+
/** Interval in seconds, when access token will expire */
8+
expires_in: number;
9+
/** The date and time when the access token expires, in ISO format */
10+
access_token_expires_on: string;
11+
/** Extended expiration time in seconds, when the access token will expire */
12+
ext_expires_in: number;
13+
/** Type */
14+
token_type: 'Bearer';
15+
/** Scopes */
16+
scope: string;
17+
/** The refresh token used to obtain a new access token */
18+
refresh_token: string;
19+
}
20+
/**
21+
* TokenRefresher class manages OAuth2 access tokens for an ioBroker adapter.
22+
*/
23+
export declare class TokenRefresher {
24+
private readonly adapter;
25+
private readonly stateName;
26+
private refreshTokenTimeout;
27+
private accessToken;
28+
private readonly url;
29+
private readonly readyPromise;
30+
private readonly name;
31+
/** Threshold in milliseconds before the access token expires to trigger a refresh */
32+
static TOKEN_REFRESH_THRESHOLD_MS: number;
33+
/**
34+
* Creates an instance of TokenRefresher.
35+
*
36+
* @param adapter Instance of ioBroker adapter
37+
* @param serviceName Name of the service for which the tokens are managed, e.g., 'spotify', 'dropbox', etc.
38+
* @param stateName Optional name of the state where tokens are stored. Defaults to 'oauth2Tokens' and that will store tokens in `ADAPTER.X.oauth2Tokens`.
39+
*/
40+
constructor(adapter: ioBroker.Adapter, serviceName: string, stateName?: string);
41+
private httpPost;
42+
private init;
43+
/**
44+
* Destroys the TokenRefresher instance, clearing any timeouts and stopping state subscriptions.
45+
*/
46+
destroy(): void;
47+
/**
48+
* This method is called when the state changes for the token.
49+
*
50+
* @param id ID of the state that changed
51+
* @param state Value
52+
*/
53+
onStateChange(id: string, state: ioBroker.State | null | undefined): void;
54+
/** Returns the access token if it is valid and not expired.*/
55+
getAccessToken(): Promise<string | undefined>;
56+
private refreshTokens;
57+
}

build/cjs/TokenRefresher.js

Lines changed: 203 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)