Skip to content

Commit

Permalink
Merge pull request #53 from zembrodt/develop
Browse files Browse the repository at this point in the history
Merge 0.5.1-dev into main
  • Loading branch information
zembrodt authored Jul 5, 2023
2 parents 13e3fd7 + 13a80e9 commit b34b8f2
Show file tree
Hide file tree
Showing 41 changed files with 1,125 additions and 418 deletions.
31 changes: 10 additions & 21 deletions README.dev.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
## ShowTunes Dev Information

### ShowTunes API
An instance of showtunes-api must be running.
The url of this instance must be provided in `src/assets/config/config.dev.json`
under `"auth" / "tokenUrl"`

Example: `"tokenUrl": "https://showtunes-api.herokuapp.com/api/auth/tokens"`

### Spotify Callbacks
The Spotify API must have a callback URL for authentications. This CAN be done via localhost,
but can also be done using a tool such as [ngrok](https://ngrok.com/) to point an external IP
to your localhost port.

To use ngrok:
- Install ngrok, set it up with your account, and configure locally
- Launch ngrok in a command window to point at your Angular app's port (default 4200): `ngrok http 4200`
- Copy the forwarding URL (example: `http://{generated subdomain}.ngrok.io`) for config
The Spotify API must have a callback URL for authentications. This CAN be done via localhost.

To launch the application:
- Add url (either `http://localhost:{port}` or ngrok url) to `config.dev.json` under `"env" / "domain"`
- Add url (`http://localhost:{port}` for example) to `config.dev.json` under `"env" / "domain"`
- Log into Spotify developer dashboard https://developer.spotify.com/dashboard
- Create app or select current one, edit settings, add `{ngrok/localhost URL}/callback` under "Redirect URIs" and save
- Add the Client ID from Spotify developer dashboard in config file as `"auth" / "clientId"`
- Create app or select current one, edit settings, add `{domain URL}/callback` under "Redirect URIs" and save

### Launching the application
- Launch Angular application as `ng serve --disable-host-check`
- Add parameter `--host 0.0.0.0` if using ngrok
- Access Angular application with ngrok or localhost URL to test changes locally
- Access Angular application with the localhost URL to test changes locally

### Testing the application
- Use `ng test` to run all unit tests
- Use `ng test --include='**/{directory}/*.spec.ts` to run all tests within a specific directory
97 changes: 95 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,99 @@

Angular web application used to display your currently playing Spotify track with a web player.

Can interface with an external API to hide Spotify client secret. See https://github.com/zembrodt/showtunes-api for an implementation in Go.
Application deployed to https://showtunes.app.

Application deployed to https://showtunes.app
Feel free to open a ticket if any issues are found.

## Authentication

Three methods are supported for Spotify authentication:
* PKCE (recommended)
* Non-PKCE (not recommended)
* Backend server

### PKCE

Send auth token requests directly to Spotify's authorization endpoint.

This will be the default authentication method if nothing is configured. It will also be used if `"auth" / "tokenUrl"` and `"auth" / "clientSecret"` are not set or
`"auth" / "forcePkce"` is set to `true`.

Required configurations will be:
- `"auth" / "cliendId"` - always required
- `"env" / "domain"` - needed to create callback URL
- `"auth" / "scopes"` - API scopes to give ShowTunes access to

This method is the recommended authentication method from Spotify and follows their flow outlines in: https://developer.spotify.com/documentation/web-api/tutorials/code-pkce-flow.

### Non-PKCE (not recommended)

Send auth token requests directly to Spotify's authorization endpoint.

This will be set as the authentication method if `"auth" / "clientSecret"` is configured, `"auth" / "tokenUrl"` is not configured, and `"auth" / "forcePkce"` is set to `false`.

The required configurations will be the same as PCKE, along with `clientSecret`:
- `"auth" / "cliendId"`
- `"auth" / "clientSecret"`
- `"env" / "domain"`
- `"auth" / "scopes"`

This method is not recommended by Spotify. It should not be used in production to reveal access to your Spotify client secret.
See https://developer.spotify.com/documentation/web-api/tutorials/code-flow for the flow used.

### Backend Server

A backend server can be used (an example implementation is provided at https://github.com/zembrodt/showtunes-api).
With this method, only the Spotify API client ID is needed to be stored in this application, with the client secret stored on the backend server.

The authentication API calls need to match requests/responses used with the official Spotify API. The only difference
is the official Spotify API returns an authentication token with the value `expires_in` as an integer value for the amount of time in milliseconds until
the token expires. This application also supports a response using `expiry` instead, as a string date for when the token will expire. The backend server can respond with either value.

The backend server will be set as the authentication method if `"auth" / "tokenUrl"` is configured and `"auth" / "forcePkce"` is set to `false`.

The required configurations will be the same as PKCE, with the addition of the third party URL as `tokenUrl`:
- `"auth" / "cliendId"`
- `"auth" / "tokenUrl"` - the full endpoint URL for the third party token endpoint
- `"env" / "domain"`
- `"auth" / "scopes"`

## Configurations

The config model can be found in the file `app-config-model.ts`. The config will be set up in a json file located in `src/assets/config`.
The name of the file needs to follow the format `config.{env_name}.json` where `env_name` is set in the environment variable `SHOWTUNES_ENV`.
If this environment variable is not set, the default value of `dev` will be used.

Explanation of configurations (**^** denotes required config):
- `"env"`
- `"name"` - (*string*) the name of the environment
- **^**`"domain"` - (*string*) the domain this app is running on
- **^**`"spotifyApiUrl"` - (*string*) Spotify's API url
- `"albumColorUrl"` - (*string*) the URL for a server to return dynamic colors (see relevant section below)
- `"auth"`
- **^**`"clientId"` (*string*) the client ID for accessing Spotify's API
- `"clientSecret"` - (*string*) the client secret for accessing Spotify's API (if using non-PKCE method)
- `"scopes"` - (*string*) comma-separated list of API Spotify scopes needed to grant the application access during OAuth
- `"tokenUrl"` - (*string*) the 3rd party backend URL for authentication if not using direct authorization with Spotify
- `"forcePkce"` - (*boolean*) used to force the application to use PKCE for authentication disregarding what other configs are set
- `"showDialog"` - (*boolean*) determines if Spotify's OAuth page is opened in a new window or not

These configurations can also be set as environment variables instead of a json file. The names for each config will be `SHOWTUNES_{configName}` where `configName` will be in
upper camel case. For example: `spotifyApiUrl` as an environment variable will be `SHOWTUNES_SPOTIFY_API_URL`.

Environment variables will always overwrite anything in the config file.

## Dynamic Colors

One feature within this application is the ability to style certain elements dynamically based on the album artwork of the
song currently playing. This functionality is enabled when its required configuration `"env" / "albumColorUrl"` is set.

The application expects the API endpoint to receive a GET request with a `url` param containing the URL to the album artwork.
The endpoint is then expected to send a response object of type `SmartColorResponse`, where `color` contains the hex color value (such as `#ABC123`).

The expected functionality of this endpoint is to analyze the image from the given URL, and send back the most common color to be used as theming within the application.

Current theming options are an exact color matched Spotify code and the selection of a material theme color that matches the dynamic color the closest. The usage of this is
configurable within the application, and these options are disabled if an `albumColorUrl` is not configured.

See the `/color` endpoint within https://github.com/zembrodt/showtunes-api for an example implementation.
10 changes: 8 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,20 @@ gulp.task('generate-config', () => {
if ('SHOWTUNES_CLIENT_ID' in process.env) {
configJson.auth.clientId = process.env.SHOWTUNES_CLIENT_ID;
}
if ('SHOWTUNES_SCOPES' in process.env) {
configJson.auth.scopes = process.env.SHOWTUNES_SCOPES;
}
if ('SHOWTUNES_FORCE_PKCE' in process.env) {
configJson.auth.forcePkce = process.env.SHOWTUNES_FORCE_PKCE;
}
if ('SHOWTUNES_CLIENT_SECRET' in process.env) {
configJson.auth.clientSecret = process.env.SHOWTUNES_CLIENT_SECRET;
}
if ('SHOWTUNES_TOKEN_URL' in process.env) {
configJson.auth.tokenUrl = process.env.SHOWTUNES_TOKEN_URL;
}
if ('SHOWTUNES_DIRECT_REQ' in process.env) {
configJson.auth.isDirectSpotifyRequest = process.env.SHOWTUNES_DIRECT_REQ === 'true';
if ('SHOWTUNES_AUTH_SHOW_DIALOG' in process.env) {
configJson.auth.showDialog = process.env.SHOWTUNES_AUTH_SHOW_DIALOG;
}
console.log('In gulp :: After OS Env: ' + JSON.stringify(configJson));

Expand Down
2 changes: 1 addition & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module.exports = function (config) {
displayName: 'ChromeHeadless'
}
},
singleRun: false,
singleRun: true,
restartOnFileChange: true
});
};
79 changes: 54 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "showtunes",
"version": "0.5.0",
"version": "0.5.1",
"scripts": {
"ng": "ng",
"start": "ng serve",
Expand Down Expand Up @@ -30,6 +30,7 @@
"@fortawesome/free-solid-svg-icons": "^5.15.2",
"@ngxs/storage-plugin": "^3.7.1",
"@ngxs/store": "^3.7.1",
"buffer": "^6.0.3",
"component": "^1.1.0",
"gulp": "^4.0.2",
"postcss": "^8.4.4",
Expand All @@ -44,7 +45,7 @@
"@ngxs/devtools-plugin": "^3.7.1",
"@types/jasmine": "~3.8.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"@types/node": "^12.20.55",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.8.0",
"jasmine-spec-reporter": "~5.0.0",
Expand Down
2 changes: 2 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ErrorComponent } from './components/error/error.component';
import { LoginComponent } from './components/login/login.component';
import { CallbackComponent } from './components/callback/callback.component';
import { DashboardComponent } from './components/dashboard/dashboard.component';
Expand All @@ -9,6 +10,7 @@ const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'callback', component: CallbackComponent },
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
{ path: 'error', component: ErrorComponent },
{ path: 'login', component: LoginComponent }
];

Expand Down
Loading

0 comments on commit b34b8f2

Please sign in to comment.