@superfan-app/spotify-auth 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -0
- package/README.md +206 -39
- package/app.plugin.js +1 -1
- package/babel.config.js +1 -0
- package/build/SpotifyAuth.types.d.ts +24 -1
- package/build/SpotifyAuth.types.d.ts.map +1 -1
- package/build/SpotifyAuth.types.js +3 -1
- package/build/SpotifyAuth.types.js.map +1 -1
- package/build/index.d.ts +5 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +21 -5
- package/build/index.js.map +1 -1
- package/expo-module.config.json +13 -0
- package/ios/SpotifyAuth.podspec +21 -4
- package/ios/SpotifyAuthAuth.swift +386 -33
- package/ios/SpotifyAuthModule.swift +94 -20
- package/package.json +20 -25
- package/plugin/build/index.d.ts +1 -1
- package/plugin/build/index.js +77 -8
- package/plugin/build/types.d.ts +65 -2
- package/plugin/src/index.ts +88 -16
- package/plugin/src/types.ts +70 -2
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/SpotifyAuth.types.ts +29 -2
- package/src/index.tsx +24 -10
- package/tsconfig.json +9 -0
- package/plugin/build/ios/withSpotifyQueryScheme.d.ts +0 -3
- package/plugin/build/ios/withSpotifyQueryScheme.js +0 -15
- package/plugin/build/ios/withSpotifyURLScheme.d.ts +0 -3
- package/plugin/build/ios/withSpotifyURLScheme.js +0 -19
- package/plugin/build/withSpotifyConfig.d.ts +0 -3
- package/plugin/build/withSpotifyConfig.js +0 -20
- package/plugin/src/ios/withSpotifyQueryScheme.ts +0 -18
- package/plugin/src/ios/withSpotifyURLScheme.ts +0 -23
- package/plugin/src/withSpotifyConfig.ts +0 -20
package/package.json
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superfan-app/spotify-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "Spotify OAuth module for Expo",
|
|
5
|
-
"main": "
|
|
6
|
-
"module": "build/index.js",
|
|
5
|
+
"main": "src/index.ts",
|
|
7
6
|
"types": "build/index.d.ts",
|
|
8
|
-
"
|
|
9
|
-
"build",
|
|
10
|
-
"ios/**/*",
|
|
11
|
-
"app.plugin.js",
|
|
12
|
-
"expo-module.config.js",
|
|
13
|
-
"plugin/**/*",
|
|
14
|
-
"src/**/*"
|
|
15
|
-
],
|
|
7
|
+
"sideEffects": false,
|
|
16
8
|
"scripts": {
|
|
17
9
|
"build": "expo-module build",
|
|
18
10
|
"clean": "expo-module clean",
|
|
@@ -20,35 +12,38 @@
|
|
|
20
12
|
"test": "expo-module test",
|
|
21
13
|
"prepare": "expo-module prepare",
|
|
22
14
|
"prepublishOnly": "expo-module prepublishOnly",
|
|
23
|
-
"
|
|
24
|
-
"expo-module": "expo-module",
|
|
25
|
-
"open:ios": "xed example/ios"
|
|
15
|
+
"expo-module": "expo-module"
|
|
26
16
|
},
|
|
27
17
|
"keywords": [
|
|
28
18
|
"react-native",
|
|
29
19
|
"expo",
|
|
30
|
-
"spotify
|
|
31
|
-
"
|
|
20
|
+
"spotify",
|
|
21
|
+
"oauth",
|
|
22
|
+
"authentication"
|
|
32
23
|
],
|
|
33
24
|
"repository": {
|
|
34
25
|
"type": "git",
|
|
35
|
-
"url": "
|
|
36
|
-
},
|
|
37
|
-
"publishConfig": {
|
|
38
|
-
"access": "public"
|
|
26
|
+
"url": "https://github.com/superfan-app/spotify-auth.git"
|
|
39
27
|
},
|
|
40
28
|
"bugs": {
|
|
41
|
-
"url": "https://github.com/
|
|
29
|
+
"url": "https://github.com/superfan-app/spotify-auth/issues"
|
|
42
30
|
},
|
|
43
|
-
"author": "Superfan
|
|
31
|
+
"author": "Superfan App",
|
|
44
32
|
"license": "MIT",
|
|
45
|
-
"
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@expo/config-plugins": "^7.0.0"
|
|
35
|
+
},
|
|
46
36
|
"devDependencies": {
|
|
47
|
-
"expo-
|
|
48
|
-
"
|
|
37
|
+
"@expo/config-types": "^49.0.0",
|
|
38
|
+
"@types/react": "^18.0.0",
|
|
39
|
+
"@types/react-native": "^0.70.0",
|
|
40
|
+
"expo-module-scripts": "^3.0.0",
|
|
41
|
+
"expo-modules-core": "^1.5.0",
|
|
42
|
+
"typescript": "^5.0.0"
|
|
49
43
|
},
|
|
50
44
|
"peerDependencies": {
|
|
51
45
|
"expo": "*",
|
|
46
|
+
"react": "*",
|
|
52
47
|
"react-native": "*"
|
|
53
48
|
}
|
|
54
49
|
}
|
package/plugin/build/index.d.ts
CHANGED
package/plugin/build/index.js
CHANGED
|
@@ -1,15 +1,84 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const config_plugins_1 = require("@expo/config-plugins");
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const pkg = require('../package.json');
|
|
5
|
+
function validateSpotifyConfig(config) {
|
|
6
|
+
if (!config.clientID)
|
|
7
|
+
throw new Error("Spotify clientID is required");
|
|
8
|
+
if (!config.scheme)
|
|
9
|
+
throw new Error("URL scheme is required");
|
|
10
|
+
if (!config.callback)
|
|
11
|
+
throw new Error("Callback path is required");
|
|
12
|
+
if (!config.tokenSwapURL)
|
|
13
|
+
throw new Error("Token swap URL is required");
|
|
14
|
+
if (!config.tokenRefreshURL)
|
|
15
|
+
throw new Error("Token refresh URL is required");
|
|
16
|
+
if (!Array.isArray(config.scopes) || config.scopes.length === 0) {
|
|
17
|
+
throw new Error("At least one scope is required");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function validateScheme(scheme) {
|
|
21
|
+
if (!scheme) {
|
|
22
|
+
throw new Error("URL scheme is required");
|
|
23
|
+
}
|
|
24
|
+
// Ensure scheme follows URL scheme naming conventions
|
|
25
|
+
if (!/^[a-z][a-z0-9+.-]*$/i.test(scheme)) {
|
|
26
|
+
throw new Error("Invalid URL scheme format. Scheme should start with a letter and contain only letters, numbers, plus, period, or hyphen.");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const withSpotifyURLSchemes = (config, props) => {
|
|
30
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
31
|
+
// Add URL scheme configuration
|
|
32
|
+
if (!config.modResults.CFBundleURLTypes) {
|
|
33
|
+
config.modResults.CFBundleURLTypes = [];
|
|
34
|
+
}
|
|
35
|
+
config.modResults.CFBundleURLTypes.push({
|
|
36
|
+
CFBundleURLSchemes: [props.scheme],
|
|
37
|
+
CFBundleURLName: props.scheme
|
|
38
|
+
});
|
|
39
|
+
// Add Spotify query scheme
|
|
40
|
+
if (!config.modResults.LSApplicationQueriesSchemes) {
|
|
41
|
+
config.modResults.LSApplicationQueriesSchemes = [];
|
|
42
|
+
}
|
|
43
|
+
if (!config.modResults.LSApplicationQueriesSchemes.includes('spotify')) {
|
|
44
|
+
config.modResults.LSApplicationQueriesSchemes.push('spotify');
|
|
45
|
+
}
|
|
46
|
+
return config;
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
const withSpotifyConfiguration = (config, props) => {
|
|
50
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
51
|
+
// Add Spotify configuration
|
|
52
|
+
config.modResults.SpotifyClientID = props.clientID;
|
|
53
|
+
config.modResults.SpotifyScheme = props.scheme;
|
|
54
|
+
config.modResults.SpotifyCallback = props.callback;
|
|
55
|
+
config.modResults.SpotifyScopes = props.scopes;
|
|
56
|
+
config.modResults.tokenRefreshURL = props.tokenRefreshURL;
|
|
57
|
+
config.modResults.tokenSwapURL = props.tokenSwapURL;
|
|
58
|
+
return config;
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const withIOSSettings = (config) => {
|
|
62
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
63
|
+
// Set minimum iOS version and build settings
|
|
64
|
+
config.modResults.MinimumOSVersion = '13.0';
|
|
65
|
+
config.modResults.EnableBitcode = false;
|
|
66
|
+
config.modResults.SwiftVersion = '5.4';
|
|
67
|
+
config.modResults.IphoneosDeploymentTarget = '13.0';
|
|
68
|
+
return config;
|
|
69
|
+
});
|
|
70
|
+
};
|
|
8
71
|
const withSpotifyAuth = (config, props) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
72
|
+
// Ensure the config exists
|
|
73
|
+
if (!props) {
|
|
74
|
+
throw new Error('Missing Spotify configuration. Please provide clientID, scheme, callback, tokenSwapURL, tokenRefreshURL, and scopes in your app.config.js/app.json.');
|
|
75
|
+
}
|
|
76
|
+
validateSpotifyConfig(props);
|
|
77
|
+
validateScheme(props.scheme);
|
|
78
|
+
// Apply configurations in sequence
|
|
79
|
+
config = withSpotifyConfiguration(config, props);
|
|
80
|
+
config = withSpotifyURLSchemes(config, props);
|
|
81
|
+
config = withIOSSettings(config);
|
|
13
82
|
return config;
|
|
14
83
|
};
|
|
15
84
|
exports.default = (0, config_plugins_1.createRunOncePlugin)(withSpotifyAuth, pkg.name, pkg.version);
|
package/plugin/build/types.d.ts
CHANGED
|
@@ -1,9 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Available Spotify authorization scopes.
|
|
3
|
+
* @see https://developer.spotify.com/documentation/general/guides/authorization/scopes/
|
|
4
|
+
*/
|
|
1
5
|
export type SpotifyScopes = 'app-remote-control' | 'playlist-modify-private' | 'playlist-modify-public' | 'playlist-read-collaborative' | 'playlist-read-private' | 'streaming' | 'user-follow-modify' | 'user-follow-read' | 'user-library-modify' | 'user-library-read' | 'user-modify-playback-state' | 'user-read-currently-playing' | 'user-read-email' | 'user-read-playback-position' | 'user-read-playback-state' | 'user-read-private' | 'user-read-recently-played' | 'user-top-read';
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for the Spotify OAuth module.
|
|
8
|
+
* This should be provided in your app.config.js or app.json under the "plugins" section.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```json
|
|
12
|
+
* {
|
|
13
|
+
* "expo": {
|
|
14
|
+
* "plugins": [
|
|
15
|
+
* [
|
|
16
|
+
* "@superfan-app/spotify-auth",
|
|
17
|
+
* {
|
|
18
|
+
* "clientID": "your_spotify_client_id",
|
|
19
|
+
* "scheme": "your-app-scheme",
|
|
20
|
+
* "callback": "callback",
|
|
21
|
+
* "tokenSwapURL": "https://your-backend.com/swap",
|
|
22
|
+
* "tokenRefreshURL": "https://your-backend.com/refresh",
|
|
23
|
+
* "scopes": ["user-read-email", "streaming"]
|
|
24
|
+
* }
|
|
25
|
+
* ]
|
|
26
|
+
* ]
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
2
31
|
export interface SpotifyConfig {
|
|
32
|
+
/**
|
|
33
|
+
* Your Spotify application's client ID.
|
|
34
|
+
* Obtain this from your Spotify Developer Dashboard.
|
|
35
|
+
* @see https://developer.spotify.com/dashboard/
|
|
36
|
+
*/
|
|
3
37
|
clientID: string;
|
|
4
|
-
|
|
5
|
-
|
|
38
|
+
/**
|
|
39
|
+
* The URL scheme to use for OAuth callbacks.
|
|
40
|
+
* This should be unique to your app and match your Spotify app settings.
|
|
41
|
+
* @example "my-spotify-app"
|
|
42
|
+
*/
|
|
6
43
|
scheme: string;
|
|
44
|
+
/**
|
|
45
|
+
* The callback path for OAuth redirects.
|
|
46
|
+
* This will be appended to your scheme to form the full redirect URI.
|
|
47
|
+
* Full URI will be: `{scheme}://{callback}`
|
|
48
|
+
* @example "callback"
|
|
49
|
+
*/
|
|
7
50
|
callback: string;
|
|
51
|
+
/**
|
|
52
|
+
* URL for token swap endpoint.
|
|
53
|
+
* This should be a secure (HTTPS) endpoint on your backend that handles
|
|
54
|
+
* the code-for-token exchange with Spotify.
|
|
55
|
+
* @example "https://your-backend.com/spotify/swap"
|
|
56
|
+
*/
|
|
57
|
+
tokenSwapURL: string;
|
|
58
|
+
/**
|
|
59
|
+
* URL for token refresh endpoint.
|
|
60
|
+
* This should be a secure (HTTPS) endpoint on your backend that handles
|
|
61
|
+
* refreshing expired access tokens.
|
|
62
|
+
* @example "https://your-backend.com/spotify/refresh"
|
|
63
|
+
*/
|
|
64
|
+
tokenRefreshURL: string;
|
|
65
|
+
/**
|
|
66
|
+
* Array of Spotify authorization scopes.
|
|
67
|
+
* These determine what permissions your app will request from users.
|
|
68
|
+
* @see SpotifyScopes for available options
|
|
69
|
+
* @example ["user-read-email", "streaming"]
|
|
70
|
+
*/
|
|
8
71
|
scopes: SpotifyScopes[];
|
|
9
72
|
}
|
package/plugin/src/index.ts
CHANGED
|
@@ -1,24 +1,96 @@
|
|
|
1
|
-
import { type ConfigPlugin, createRunOncePlugin } from '@expo/config-plugins'
|
|
1
|
+
import { type ConfigPlugin, createRunOncePlugin, withInfoPlist } from '@expo/config-plugins'
|
|
2
|
+
import { SpotifyConfig } from './types.js'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
import { withSpotifyURLScheme } from './ios/withSpotifyURLScheme'
|
|
5
|
-
import { SpotifyConfig } from './types'
|
|
6
|
-
import { withSpotifyOAuthConfig } from './withSpotifyConfig'
|
|
4
|
+
const pkg = require('../package.json');
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
function validateSpotifyConfig(config: SpotifyConfig) {
|
|
7
|
+
if (!config.clientID) throw new Error("Spotify clientID is required")
|
|
8
|
+
if (!config.scheme) throw new Error("URL scheme is required")
|
|
9
|
+
if (!config.callback) throw new Error("Callback path is required")
|
|
10
|
+
if (!config.tokenSwapURL) throw new Error("Token swap URL is required")
|
|
11
|
+
if (!config.tokenRefreshURL) throw new Error("Token refresh URL is required")
|
|
12
|
+
if (!Array.isArray(config.scopes) || config.scopes.length === 0) {
|
|
13
|
+
throw new Error("At least one scope is required")
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function validateScheme(scheme: string) {
|
|
18
|
+
if (!scheme) {
|
|
19
|
+
throw new Error("URL scheme is required");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Ensure scheme follows URL scheme naming conventions
|
|
23
|
+
if (!/^[a-z][a-z0-9+.-]*$/i.test(scheme)) {
|
|
24
|
+
throw new Error("Invalid URL scheme format. Scheme should start with a letter and contain only letters, numbers, plus, period, or hyphen.");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const withSpotifyURLSchemes: ConfigPlugin<SpotifyConfig> = (config, props) => {
|
|
29
|
+
return withInfoPlist(config, (config) => {
|
|
30
|
+
// Add URL scheme configuration
|
|
31
|
+
if (!config.modResults.CFBundleURLTypes) {
|
|
32
|
+
config.modResults.CFBundleURLTypes = [];
|
|
33
|
+
}
|
|
34
|
+
config.modResults.CFBundleURLTypes.push({
|
|
35
|
+
CFBundleURLSchemes: [props.scheme],
|
|
36
|
+
CFBundleURLName: props.scheme
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Add Spotify query scheme
|
|
40
|
+
if (!config.modResults.LSApplicationQueriesSchemes) {
|
|
41
|
+
config.modResults.LSApplicationQueriesSchemes = [];
|
|
42
|
+
}
|
|
43
|
+
if (!config.modResults.LSApplicationQueriesSchemes.includes('spotify')) {
|
|
44
|
+
config.modResults.LSApplicationQueriesSchemes.push('spotify');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return config;
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const withSpotifyConfiguration: ConfigPlugin<SpotifyConfig> = (config, props) => {
|
|
52
|
+
return withInfoPlist(config, (config) => {
|
|
53
|
+
// Add Spotify configuration
|
|
54
|
+
config.modResults.SpotifyClientID = props.clientID;
|
|
55
|
+
config.modResults.SpotifyScheme = props.scheme;
|
|
56
|
+
config.modResults.SpotifyCallback = props.callback;
|
|
57
|
+
config.modResults.SpotifyScopes = props.scopes;
|
|
58
|
+
config.modResults.tokenRefreshURL = props.tokenRefreshURL;
|
|
59
|
+
config.modResults.tokenSwapURL = props.tokenSwapURL;
|
|
60
|
+
|
|
61
|
+
return config;
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const withIOSSettings: ConfigPlugin = (config) => {
|
|
66
|
+
return withInfoPlist(config, (config) => {
|
|
67
|
+
// Set minimum iOS version and build settings
|
|
68
|
+
config.modResults.MinimumOSVersion = '13.0';
|
|
69
|
+
config.modResults.EnableBitcode = false;
|
|
70
|
+
config.modResults.SwiftVersion = '5.4';
|
|
71
|
+
config.modResults.IphoneosDeploymentTarget = '13.0';
|
|
72
|
+
|
|
73
|
+
return config;
|
|
74
|
+
});
|
|
75
|
+
};
|
|
13
76
|
|
|
14
77
|
const withSpotifyAuth: ConfigPlugin<SpotifyConfig> = (config, props) => {
|
|
15
|
-
|
|
78
|
+
// Ensure the config exists
|
|
79
|
+
if (!props) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
'Missing Spotify configuration. Please provide clientID, scheme, callback, tokenSwapURL, tokenRefreshURL, and scopes in your app.config.js/app.json.'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
16
84
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
config = withSpotifyURLScheme(config, props)
|
|
85
|
+
validateSpotifyConfig(props);
|
|
86
|
+
validateScheme(props.scheme);
|
|
20
87
|
|
|
21
|
-
|
|
22
|
-
|
|
88
|
+
// Apply configurations in sequence
|
|
89
|
+
config = withSpotifyConfiguration(config, props);
|
|
90
|
+
config = withSpotifyURLSchemes(config, props);
|
|
91
|
+
config = withIOSSettings(config);
|
|
92
|
+
|
|
93
|
+
return config;
|
|
94
|
+
};
|
|
23
95
|
|
|
24
96
|
export default createRunOncePlugin(withSpotifyAuth, pkg.name, pkg.version);
|
package/plugin/src/types.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Available Spotify authorization scopes.
|
|
3
|
+
* @see https://developer.spotify.com/documentation/general/guides/authorization/scopes/
|
|
4
|
+
*/
|
|
1
5
|
export type SpotifyScopes =
|
|
2
6
|
| 'app-remote-control'
|
|
3
7
|
| 'playlist-modify-private'
|
|
@@ -18,11 +22,75 @@ export type SpotifyScopes =
|
|
|
18
22
|
| 'user-read-recently-played'
|
|
19
23
|
| 'user-top-read'
|
|
20
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Configuration options for the Spotify OAuth module.
|
|
27
|
+
* This should be provided in your app.config.js or app.json under the "plugins" section.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```json
|
|
31
|
+
* {
|
|
32
|
+
* "expo": {
|
|
33
|
+
* "plugins": [
|
|
34
|
+
* [
|
|
35
|
+
* "@superfan-app/spotify-auth",
|
|
36
|
+
* {
|
|
37
|
+
* "clientID": "your_spotify_client_id",
|
|
38
|
+
* "scheme": "your-app-scheme",
|
|
39
|
+
* "callback": "callback",
|
|
40
|
+
* "tokenSwapURL": "https://your-backend.com/swap",
|
|
41
|
+
* "tokenRefreshURL": "https://your-backend.com/refresh",
|
|
42
|
+
* "scopes": ["user-read-email", "streaming"]
|
|
43
|
+
* }
|
|
44
|
+
* ]
|
|
45
|
+
* ]
|
|
46
|
+
* }
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
21
50
|
export interface SpotifyConfig {
|
|
51
|
+
/**
|
|
52
|
+
* Your Spotify application's client ID.
|
|
53
|
+
* Obtain this from your Spotify Developer Dashboard.
|
|
54
|
+
* @see https://developer.spotify.com/dashboard/
|
|
55
|
+
*/
|
|
22
56
|
clientID: string
|
|
23
|
-
|
|
24
|
-
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The URL scheme to use for OAuth callbacks.
|
|
60
|
+
* This should be unique to your app and match your Spotify app settings.
|
|
61
|
+
* @example "my-spotify-app"
|
|
62
|
+
*/
|
|
25
63
|
scheme: string
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The callback path for OAuth redirects.
|
|
67
|
+
* This will be appended to your scheme to form the full redirect URI.
|
|
68
|
+
* Full URI will be: `{scheme}://{callback}`
|
|
69
|
+
* @example "callback"
|
|
70
|
+
*/
|
|
26
71
|
callback: string
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* URL for token swap endpoint.
|
|
75
|
+
* This should be a secure (HTTPS) endpoint on your backend that handles
|
|
76
|
+
* the code-for-token exchange with Spotify.
|
|
77
|
+
* @example "https://your-backend.com/spotify/swap"
|
|
78
|
+
*/
|
|
79
|
+
tokenSwapURL: string
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* URL for token refresh endpoint.
|
|
83
|
+
* This should be a secure (HTTPS) endpoint on your backend that handles
|
|
84
|
+
* refreshing expired access tokens.
|
|
85
|
+
* @example "https://your-backend.com/spotify/refresh"
|
|
86
|
+
*/
|
|
87
|
+
tokenRefreshURL: string
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Array of Spotify authorization scopes.
|
|
91
|
+
* These determine what permissions your app will request from users.
|
|
92
|
+
* @see SpotifyScopes for available options
|
|
93
|
+
* @example ["user-read-email", "streaming"]
|
|
94
|
+
*/
|
|
27
95
|
scopes: SpotifyScopes[]
|
|
28
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/index.ts","./src/types.ts"
|
|
1
|
+
{"root":["./src/index.ts","./src/types.ts"],"version":"5.7.2"}
|
package/src/SpotifyAuth.types.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { createContext } from "react";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Event data structure for Spotify authorization events
|
|
5
|
+
*/
|
|
6
|
+
export interface SpotifyAuthEvent {
|
|
7
|
+
success: boolean;
|
|
8
|
+
token: string | null;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
3
12
|
/**
|
|
4
13
|
* Data returned from the Spotify authorization process
|
|
5
14
|
*/
|
|
@@ -12,6 +21,18 @@ export interface SpotifyAuthorizationData {
|
|
|
12
21
|
error?: string;
|
|
13
22
|
}
|
|
14
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Configuration for the authorization request
|
|
26
|
+
*/
|
|
27
|
+
export interface AuthorizeConfig {
|
|
28
|
+
/** Spotify Client ID */
|
|
29
|
+
clientId: string;
|
|
30
|
+
/** OAuth redirect URL */
|
|
31
|
+
redirectUrl: string;
|
|
32
|
+
/** Whether to show the auth dialog */
|
|
33
|
+
showDialog?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
15
36
|
/**
|
|
16
37
|
* Props for the SpotifyAuthView component
|
|
17
38
|
*/
|
|
@@ -27,12 +48,18 @@ export interface SpotifyAuthContext {
|
|
|
27
48
|
/** The current Spotify access token, null if not authenticated */
|
|
28
49
|
accessToken: string | null;
|
|
29
50
|
/** Function to initiate Spotify authorization */
|
|
30
|
-
authorize: () => void
|
|
51
|
+
authorize: (config: AuthorizeConfig) => Promise<void>;
|
|
52
|
+
/** Whether authorization is in progress */
|
|
53
|
+
isAuthenticating: boolean;
|
|
54
|
+
/** Last error that occurred during authentication */
|
|
55
|
+
error: string | null;
|
|
31
56
|
}
|
|
32
57
|
|
|
33
58
|
export const SpotifyAuthContextInstance = createContext<SpotifyAuthContext>({
|
|
34
59
|
accessToken: null,
|
|
35
|
-
authorize: () => {
|
|
60
|
+
authorize: async () => {},
|
|
61
|
+
isAuthenticating: false,
|
|
62
|
+
error: null,
|
|
36
63
|
});
|
|
37
64
|
|
|
38
65
|
export interface SpotifyAuthOptions {
|
package/src/index.tsx
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import { EventEmitter } from "expo-modules-core";
|
|
2
|
-
import React, { useContext, useEffect, useState } from "react";
|
|
2
|
+
import React, { useContext, useEffect, useState, useCallback } from "react";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
SpotifyAuthorizationData,
|
|
6
6
|
SpotifyAuthContext,
|
|
7
7
|
SpotifyAuthContextInstance,
|
|
8
|
+
type AuthorizeConfig,
|
|
8
9
|
} from "./SpotifyAuth.types";
|
|
9
10
|
import SpotifyAuthModule from "./SpotifyAuthModule";
|
|
10
11
|
|
|
11
12
|
// First define the event name as a string literal type
|
|
12
13
|
type SpotifyAuthEventName = "onSpotifyAuth"; // This should match SpotifyAuthModule.AuthEventName
|
|
13
14
|
|
|
14
|
-
// Then use that type for events
|
|
15
|
-
type SpotifyEvents = {
|
|
16
|
-
[K in SpotifyAuthEventName]: (data: SpotifyAuthorizationData) => void;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
15
|
// Create a properly typed emitter
|
|
20
|
-
const emitter = new EventEmitter
|
|
16
|
+
const emitter = new EventEmitter(SpotifyAuthModule);
|
|
21
17
|
|
|
22
18
|
function addAuthListener(listener: (data: SpotifyAuthorizationData) => void) {
|
|
23
19
|
// Assert the event name is of the correct type
|
|
@@ -28,8 +24,8 @@ function addAuthListener(listener: (data: SpotifyAuthorizationData) => void) {
|
|
|
28
24
|
/**
|
|
29
25
|
* Prompts the user to log in to Spotify and authorize your application.
|
|
30
26
|
*/
|
|
31
|
-
function authorize(): void {
|
|
32
|
-
SpotifyAuthModule.authorize();
|
|
27
|
+
export function authorize(config: AuthorizeConfig): void {
|
|
28
|
+
SpotifyAuthModule.authorize(config);
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
interface SpotifyAuthProviderProps {
|
|
@@ -40,6 +36,24 @@ export function SpotifyAuthProvider({
|
|
|
40
36
|
children,
|
|
41
37
|
}: SpotifyAuthProviderProps): JSX.Element {
|
|
42
38
|
const [token, setToken] = useState<string | null>(null);
|
|
39
|
+
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
|
40
|
+
const [error, setError] = useState<string | null>(null);
|
|
41
|
+
|
|
42
|
+
const authorize = useCallback(
|
|
43
|
+
async (config: AuthorizeConfig): Promise<void> => {
|
|
44
|
+
try {
|
|
45
|
+
setIsAuthenticating(true);
|
|
46
|
+
setError(null);
|
|
47
|
+
await SpotifyAuthModule.authorize(config);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
setError(err instanceof Error ? err.message : "Authorization failed");
|
|
50
|
+
throw err;
|
|
51
|
+
} finally {
|
|
52
|
+
setIsAuthenticating(false);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
[],
|
|
56
|
+
);
|
|
43
57
|
|
|
44
58
|
useEffect(() => {
|
|
45
59
|
const subscription = addAuthListener((data) => {
|
|
@@ -53,7 +67,7 @@ export function SpotifyAuthProvider({
|
|
|
53
67
|
|
|
54
68
|
return (
|
|
55
69
|
<SpotifyAuthContextInstance.Provider
|
|
56
|
-
value={{ accessToken: token, authorize }}
|
|
70
|
+
value={{ accessToken: token, authorize, isAuthenticating, error }}
|
|
57
71
|
>
|
|
58
72
|
{children}
|
|
59
73
|
</SpotifyAuthContextInstance.Provider>
|
package/tsconfig.json
ADDED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withSpotifyQueryScheme = void 0;
|
|
4
|
-
const config_plugins_1 = require("expo/config-plugins");
|
|
5
|
-
const spotifyScheme = "spotify";
|
|
6
|
-
const withSpotifyQueryScheme = (config) => (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
7
|
-
if (!config.modResults.LSApplicationQueriesSchemes) {
|
|
8
|
-
config.modResults.LSApplicationQueriesSchemes = [];
|
|
9
|
-
}
|
|
10
|
-
if (!config.modResults.LSApplicationQueriesSchemes.includes(spotifyScheme)) {
|
|
11
|
-
config.modResults.LSApplicationQueriesSchemes.push(spotifyScheme);
|
|
12
|
-
}
|
|
13
|
-
return config;
|
|
14
|
-
});
|
|
15
|
-
exports.withSpotifyQueryScheme = withSpotifyQueryScheme;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withSpotifyURLScheme = void 0;
|
|
4
|
-
const config_plugins_1 = require("expo/config-plugins");
|
|
5
|
-
const withSpotifyURLScheme = (config, { scheme }) => {
|
|
6
|
-
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
7
|
-
const bundleId = config.ios?.bundleIdentifier;
|
|
8
|
-
const urlType = {
|
|
9
|
-
CFBundleURLName: bundleId,
|
|
10
|
-
CFBundleURLSchemes: [scheme],
|
|
11
|
-
};
|
|
12
|
-
if (!config.modResults.CFBundleURLTypes) {
|
|
13
|
-
config.modResults.CFBundleURLTypes = [];
|
|
14
|
-
}
|
|
15
|
-
config.modResults.CFBundleURLTypes.push(urlType);
|
|
16
|
-
return config;
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
exports.withSpotifyURLScheme = withSpotifyURLScheme;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withSpotifyOAuthConfig = void 0;
|
|
4
|
-
const config_plugins_1 = require("expo/config-plugins");
|
|
5
|
-
const withSpotifyOAuthConfigIOS = (config, spotifyConfig) => {
|
|
6
|
-
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
7
|
-
config.modResults.SpotifyClientID = spotifyConfig.clientID;
|
|
8
|
-
config.modResults.SpotifyScheme = spotifyConfig.scheme;
|
|
9
|
-
config.modResults.SpotifyCallback = spotifyConfig.callback;
|
|
10
|
-
config.modResults.SpotifyScopes = spotifyConfig.scopes;
|
|
11
|
-
config.modResults.tokenRefreshURL = spotifyConfig.tokenRefreshURL;
|
|
12
|
-
config.modResults.tokenSwapURL = spotifyConfig.tokenSwapURL;
|
|
13
|
-
return config;
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
const withSpotifyOAuthConfig = (config, spotifyConfig) => {
|
|
17
|
-
config = withSpotifyOAuthConfigIOS(config, spotifyConfig);
|
|
18
|
-
return config;
|
|
19
|
-
};
|
|
20
|
-
exports.withSpotifyOAuthConfig = withSpotifyOAuthConfig;
|