chore: consolidate into single component file.

This commit is contained in:
Darrel O'Pry 2020-08-12 19:58:59 -04:00
parent 701668b662
commit 215a0a443f
3 changed files with 131 additions and 129 deletions

View file

@ -1,55 +1,108 @@
<script> <script context="module">
import { writable } from 'svelte/store';
import { getContext } from 'svelte';
import oidcClient from 'oidc-client'; import oidcClient from 'oidc-client';
const { UserManager } = oidcClient; const { UserManager } = oidcClient;
import { onMount, onDestroy, setContext, getContext } from 'svelte'; import { onMount, onDestroy, setContext } from 'svelte';
import {
OIDC_CONTEXT_REDIRECT_URI,
OIDC_CONTEXT_CLIENT_PROMISE,
OIDC_CONTEXT_POST_LOGOUT_REDIRECT_URI,
idToken,
accessToken,
isAuthenticated,
isLoading,
authError,
userInfo,
} from './oidc';
// props. /**
export let issuer; * Stores
export let client_id; */
export let redirect_uri; export const isLoading = writable(true);
export let post_logout_redirect_uri; export const isAuthenticated = writable(false);
export let metadata = {}; export const accessToken = writable('');
export const idToken = writable('');
export const userInfo = writable({});
export const authError = writable(null);
setContext(OIDC_CONTEXT_REDIRECT_URI, redirect_uri); /**
setContext(OIDC_CONTEXT_POST_LOGOUT_REDIRECT_URI, post_logout_redirect_uri); * Context Keys
*
* using an object literal means the keys are guaranteed not to conflict in any circumstance (since an object only has
* referential equality to itself, i.e. {} !== {} whereas "x" === "x"), even when you have multiple different contexts
* operating across many component layers.
*/
export const OIDC_CONTEXT_CLIENT_PROMISE = {};
export const OIDC_CONTEXT_REDIRECT_URI = {};
export const OIDC_CONTEXT_POST_LOGOUT_REDIRECT_URI = {};
// getContext doesn't seem to return a value in OnMount, so we'll pass the oidcPromise around by reference. /**
const settings = { * Refresh the accessToken store.
authority: issuer, */
client_id, export async function refreshToken() {
response_type: 'id_token token', const oidc = await getContext(OIDC_CONTEXT_CLIENT_PROMISE);
redirect_uri, await oidc.signinSilent();
post_logout_redirect_uri, }
response_type: 'code',
scope: 'openid profile email', /**
automaticSilentRenew: true, * Initiate Register/Login flow.
*
* @param {boolean} preserveRoute - store current location so callback handler will navigate back to it.
* @param {string} callback_url - explicit path to use for the callback.
*/
export async function login(preserveRoute = true, callback_url = null) {
const oidc = await getContext(OIDC_CONTEXT_CLIENT_PROMISE);
const redirect_uri = callback_url || getContext(OIDC_CONTEXT_REDIRECT_URI) || window.location.href;
// try to keep the user on the same page from which they triggered login. If set to false should typically
// cause redirect to /.
const appState = preserveRoute
? {
pathname: window.location.pathname,
search: window.location.search,
}
: {};
await oidc.signinRedirect({ redirect_uri, appState });
}
/**
* Log out the current user.
*
* @param {string} logout_url - specify the url to return to after login.
*/
export async function logout(logout_url = null) {
const oidc = await getContext(OIDC_CONTEXT_CLIENT_PROMISE);
const returnTo = logout_url || getContext(OIDC_CONTEXT_POST_LOGOUT_REDIRECT_URI) || window.location.href;
oidc.signoutRedirect({ returnTo });
}
</script>
<script>
// props.
export let issuer;
export let client_id;
export let redirect_uri;
export let post_logout_redirect_uri;
export let metadata = {};
setContext(OIDC_CONTEXT_REDIRECT_URI, redirect_uri);
setContext(OIDC_CONTEXT_POST_LOGOUT_REDIRECT_URI, post_logout_redirect_uri);
const settings = {
authority: issuer,
client_id,
// response_type: 'id_token token',
redirect_uri,
post_logout_redirect_uri,
response_type: 'code',
scope: 'openid profile email',
automaticSilentRenew: true,
metadata metadata
}; };
const userManager = new UserManager(settings); const userManager = new UserManager(settings);
userManager.events.addUserLoaded(function (user) { userManager.events.addUserLoaded(function (user) {
isAuthenticated.set(true); isAuthenticated.set(true);
accessToken.set(user.access_token); accessToken.set(user.access_token);
idToken.set(user.id_token); idToken.set(user.id_token);
userInfo.set(user.profile); userInfo.set(user.profile);
}); });
userManager.events.addUserUnloaded(function () { userManager.events.addUserUnloaded(function () {
isAuthenticated.set(false); isAuthenticated.set(false);
idToken.set(''); idToken.set('');
accessToken.set(''); accessToken.set('');
userInfo.set({}); userInfo.set({});
}); });
userManager.events.addSilentRenewError(function (e) { userManager.events.addSilentRenewError(function (e) {
@ -61,40 +114,40 @@
setContext(OIDC_CONTEXT_CLIENT_PROMISE, oidcPromise); setContext(OIDC_CONTEXT_CLIENT_PROMISE, oidcPromise);
async function handleOnMount() { async function handleOnMount() {
// on run onMount after oidc // on run onMount after oidc
const oidc = await oidcPromise; const oidc = await oidcPromise;
// Not all browsers support this, please program defensively! // Not all browsers support this, please program defensively!
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
// Check if something went wrong during login redirect // Check if something went wrong during login redirect
// and extract the error message // and extract the error message
if (params.has('error')) { if (params.has('error')) {
authError.set(new Error(params.get('error_description'))); authError.set(new Error(params.get('error_description')));
} }
// if code then login success // if code then login success
if (params.has('code')) { if (params.has('code')) {
// handle the callback // handle the callback
const response = await oidc.signinCallback(); const response = await oidc.signinCallback();
let state = (response && response.state) || {} let state = (response && response.state) || {}
// Can be smart here and redirect to original path instead of root // Can be smart here and redirect to original path instead of root
const url = state && state.targetUrl ? state.targetUrl : window.location.pathname; const url = state && state.targetUrl ? state.targetUrl : window.location.pathname;
state = { ...state, isRedirectCallback: true }; state = { ...state, isRedirectCallback: true };
// redirect to the last page we were on when login was configured if it was passed. // redirect to the last page we were on when login was configured if it was passed.
history.replaceState(state, "", url); history.replaceState(state, "", url);
// location.href = url; // location.href = url;
// clear errors on login. // clear errors on login.
authError.set(null); authError.set(null);
} }
isLoading.set(false); isLoading.set(false);
} }
async function handleOnDestroy() {} async function handleOnDestroy() {}
onMount(handleOnMount); onMount(handleOnMount);
onDestroy(handleOnDestroy); onDestroy(handleOnDestroy);
</script> </script>

View file

@ -1,3 +1,13 @@
export * from './oidc' export {
export { default as OidcContext } from './OidcContext.svelte'; default as OidcContext,
authError,
idToken,
accessToken,
isAuthenticated,
isLoading,
login,
logout,
refreshToken,
userInfo,
} from './OidcContext.svelte';

View file

@ -1,61 +0,0 @@
import { writable } from 'svelte/store';
import { getContext } from 'svelte';
/**
* Stores
*/
export const isLoading = writable(true);
export const isAuthenticated = writable(false);
export const accessToken = writable('');
export const idToken = writable('');
export const userInfo = writable({});
export const authError = writable(null);
/**
* Context Keys
*
* using an object literal means the keys are guaranteed not to conflict in any circumstance (since an object only has
* referential equality to itself, i.e. {} !== {} whereas "x" === "x"), even when you have multiple different contexts
* operating across many component layers.
*/
export const OIDC_CONTEXT_CLIENT_PROMISE = {};
export const OIDC_CONTEXT_REDIRECT_URI = {};
export const OIDC_CONTEXT_POST_LOGOUT_REDIRECT_URI = {};
/**
* Refresh the accessToken store.
*/
export async function refreshToken() {
const oidc = await getContext(OIDC_CONTEXT_CLIENT_PROMISE)
const token = await oidc.signinSilent();
accessToken.set(token.accessToken);
idToken.set(token.idToken);
}
/**
* Initiate Register/Login flow.
*
* @param {boolean} preserveRoute - store current location so callback handler will navigate back to it.
* @param {string} callback_url - explicit path to use for the callback.
*/
export async function login(preserveRoute = true, callback_url = null) {
const oidc = await getContext(OIDC_CONTEXT_CLIENT_PROMISE)
const redirect_uri = callback_url || getContext(OIDC_CONTEXT_REDIRECT_URI) || window.location.href;
// try to keep the user on the same page from which they triggered login. If set to false should typically
// cause redirect to /.
const appState = (preserveRoute) ? { pathname: window.location.pathname, search: window.location.search } : {}
await oidc.signinRedirect({ redirect_uri, appState });
}
/**
* Log out the current user.
*
* @param {string} logout_url - specify the url to return to after login.
*/
export async function logout(logout_url = null) {
const oidc = await getContext(OIDC_CONTEXT_CLIENT_PROMISE)
const returnTo = logout_url || getContext(OIDC_CONTEXT_POST_LOGOUT_REDIRECT_URI) || window.location.href;
accessToken.set('');
oidc.signoutRedirect({ returnTo });
}