Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Makefile.inc
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
CLIENT_ID = PUT_YOUR_CLIENT_ID_HERE
CLIENT_SECRET = PUT_YOUR_CLIENT_SECRET_HERE
TENANT_ID = your-tenant
SERVER_ID = your-workspace-id
ISSUER_URL = https://your-tenant.your-region.authz.cloudentity.io/your-tenant/admin
SERVER_ID = your-workspace-id
THEME_ID = demo
THEME_DIR = theme
#
# Set TENANT_ID and ISSUER_URL appropriately for your client.
# Your client should be in the 'admin' workspace, but SERVER_ID can be any workspace
Expand Down Expand Up @@ -66,15 +67,15 @@ delete-theme: ## Delete a theme
--header 'Authorization: Bearer ${TOKEN}'

upsert-templates: ## Insert or Update all templates
for f in $$(cd theme; find * -name '*.tmpl'); \
for f in $$(cd ${THEME_DIR}; find * -name '*.tmpl'); \
do make upsert-template THEME_ID=${THEME_ID} TEMPLATE_PATH="$$f" TOKEN=${TOKEN} ; \
done

upsert-template: ## Insert or Update one template (make upsert-template TEMPLATE_PATH=pages/authorization/login/scripts.tmpl)
${CURL} -D - -X PUT '${BASEURL}/api/admin/${TENANT_ID}/theme/${THEME_ID}/template/${TEMPLATE_PATH}' \
--header 'Authorization: Bearer ${TOKEN}' \
--header 'Content-Type: application/json' \
--data-binary @<(jq -M --raw-input --slurp < 'theme/${TEMPLATE_PATH}' '{"content":.}')
--data-binary @<(jq -M --raw-input --slurp < '${THEME_DIR}/${TEMPLATE_PATH}' '{"content":.}')

delete-template: ## Delete a template (make delete-template TEMPLATE_PATH=pages/authorization/login/scripts.tmpl)
${CURL} -D - -X DELETE '${BASEURL}/api/admin/${TENANT_ID}/theme/${THEME_ID}/template/${TEMPLATE_PATH}' \
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ If a situation comes up in which it's absolutely necessary for someone to make a

## How to use the Makefile/CLI

Prerequisites:
- `jq` - A command line [JSON processor](https://github.com/stedolan/jq).

You will need to create an ACP Client named 'manage-themes' in the Admin workspace with the application type `Service`.

> Note: If the `Service` application type is not available, in the Admin workspace, navigate to "Auth Setttings > OAuth," and under "Allowed Grant types," make sure "Client credentials" is selected.
> **Note:** If the `Service` application type is not available, in the Admin workspace, navigate to "Auth Settings > OAuth," and under "Allowed Grant types," make sure "Client credentials" is selected.

Confirm these OAuth settings after you have created the client:

Expand All @@ -40,11 +43,14 @@ Now create a file named `Makefile.inc` that overrides the following default valu
CLIENT_ID = PUT_YOUR_CLIENT_ID_HERE
CLIENT_SECRET = PUT_YOUR_CLIENT_SECRET_HERE
TENANT_ID = your-tenant
SERVER_ID = your-workspace-id
ISSUER_URL = https://your-tenant.your-region.authz.cloudentity.io/your-tenant/admin
SERVER_ID = your-workspace-id
THEME_ID = demo
THEME_DIR = theme
```

> **Note:** The `ISSUER_URL` is related to the OAuth client in the Admin workspace, thus the path pattern is `your-tenant/admin`. The `SERVER_ID` refers to the ID of the **workspace** to which the theme is to be bound. **NEVER** put `admin` as the value for `SERVER_ID`, as using a custom theme that is bound to the admin workspace has the potential to lock you out of your tenant if you make an edit in the custom theme templates that results in certain types of errors. Always use a dedicated test workspace for actively developing the theme.

**IMPORTANT:** When working with a "vanity domain" that does not have a tenant ID in the URL params for API requests, leave the value of the `TENANT_ID` variable blank in the `Makefile.inc`, and set the value of `ISSUER_URL` without the `TENANT_ID` param, as shown below:

```
Expand All @@ -54,6 +60,8 @@ ISSUER_URL = https://vanity-domain.your-organization.com/admin

> **Note:** `SERVER_ID` refers to the ID of the workspace to which you intend to bind your theme.

The `THEME_DIR` value indicates which directory should be used as the local source of the templates when 'upsert' commands are used. When creating a custom theme, it is possible to leave the default theme unedited as a reference, and set up a custom theme directory with a copy of the whole theme (or containing only a subset of templates to be altered, as long as the directory structure matches that of the default theme). In this case, change the value of `THEME_DIR` to the path of your custom directory. For example, this could be something like `my-custom-themes/demo-theme-1`, where `my-custom-themes` is located in the project root, and with `demo-theme-1` containing the `pages` and `shared` directories, etc.

To test that the makefile is working, you can do `make list-base-templates` to query ACP:
```
$ make list-base-templates
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{{ template "base" . }}

{{ define "title" }}Demo{{ end }}

{{ define "content" }}
{{ template "demo_scripts" . }}

{{ if .Data.FlowCompleted }}
<div class="header-container">
{{ template "header" .Styling }}

<a href="?" class="logout-container">
<button class="submit-button" type="button" id="sign-in">Sign out</button>
</a>
</div>

<div class="custom-header">
<div>
<h2>Demo Application</h2>

<div class="demo-container">
<div class="demo-info">
<i class="material-icons">error_outline</i>
<div class="demo-info-text">
<p><b>You have logged in to the Demo app</b></p>
<p>
The information below is used for demo and testing purposes.
You can view aggregated claims or the tokens issued by ACP below.
</p>
</div>
</div>
</div>

<div class="tabs-container">
<button id="claims-tab" role="tab" data-tabid="claims" class="tab-button tab-button-active">
Claims
</button>

<button id="access-token-tab" role="tab" data-tabid="access-token" class="tab-button">
Access Token
</button>

{{ if .Data.IDTokenRaw }}
<button id="id-token-tab" role="tab" data-tabid="id-token" class="tab-button">
ID Token
</button>
{{ end }}

{{ if .Data.RefreshTokenRaw }}
<button id="refresh-token-tab" role="tab" data-tabid="refresh-token" class="tab-button">
Refresh Token
</button>
{{ end }}
</div>
</div>
</div>

<div class="demo-content">
{{ template "demo-claims-tab" . }}
{{ template "demo-access-token-tab" . }}
{{ template "demo-id-token-tab" . }}
{{ template "demo-refresh-token-tab" . }}
</div>

{{ else }}
<div class="header-container">
{{ template "header" .Config }}
</div>

<div class="card">

<h3 class="header">Demo {{ .flow }} application</h3>

{{ if eq .Data.Flow "device" }}
{{ template "demo-unauthorized" . }}
{{ else }}
{{ template "demo-login" . }}
{{ end }}
</div>
{{ end }}
{{ end }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{{ template "base" . }}

{{ define "title" }} Login {{ end }}

{{ define "content" }}
{{ template "login_scripts" . }}
{{ template "password_visibility_script" . }}

<div class="content-wrapper">
<div class="content-flex" style="height: 100%;">
<div class="content-left">
<div class="content-background"></div>
</div>

<div class="content-right">
<div class="aut-content-wrapper">
<div class="header-container">
{{ template "header" .Config }}
</div>

<div class="card-right">
{{ if not .Data.HasIDPs }}
{{ template "login_no_idp" . }}
{{ else }}
<div id="content-container">
<div class="card-right">
{{ if .Data.HasAdminTabs }}
{{ template "login_admin_tabs" }}
{{ end }}

<div id="sign-in">
<div class="content">
{{ template "login_content" . }}
</div>
</div>

{{ template "login_admin_quick_access" . }}
</div>

<div class="cancel-container">
<a href="?login_id={{ .Data.SessionID }}&action=cancel" id="cancel" name="cancel">Cancel</a>
</div>
</div>
{{ end }}
</div>

{{ template "footer" }}

</div>
</div>
</div>
</div>
{{ end }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
{{ define "login_content_mode_idps_list" }}

<form action="?login_id={{ .Data.SessionID }}&prompt={{ .Data.Prompt }}&max_age={{ .Data.MaxAge }}" method="post" id="sign-in-form">
<input type="hidden" id="version" name="version" value="2">

<div class="error-container non-visible" id="idps-discovery-error">
<i class="material-icons">error_outline</i>
Failed to fetch IDPs
</div>

<div class="error-container non-visible" id="idps-empty-error">
<i class="material-icons">error_outline</i>
Authentication is not supported for this user/domain
</div>

{{ if .Data.RememberedSignInMethod }}
{{ template "login_content_remembered_idp" . }}
{{ else }}
{{ if .Data.IDPDiscoveryEnabled }}
<h2 id="log-in-header">Sign into your account</h2>
<div id="username-password-form">
{{ .Data.CsrfField }}

<div class="form-field">
<label for="text-field-username-input" class="label-with-link">
Username | Email | Phone
<button id="not-you-button" class="non-visible" type="button">Not You?</button>
</label>
<input id="text-field-username-input" name="username" autocomplete="off" data-lpignore="true" value="{{ .Data.Identifier }}">
</div>

<button class="submit-button" type="button" id="sign-in-button" disabled>
<span>Next</span>
<div class="non-visible">{{ template "loader" }}</div>
</button>
</div>

{{ else }}
<h2 id="log-in-header" class="header-small-margin">Select how you want to sign in</h2>
<input type="hidden" name="username" value="{{ .Data.Identifier }}">
<input type="hidden" name="password" value="">
{{ end }}
{{ end }}

<div
id="idps-list-header"
class="spacer-container {{ if .Data.HasNonHiddenIDP }}visible{{ else }}non-visible{{ end }}"
{{ if .Data.HasNonHiddenIDP }}
data-default-style-display="block"
{{ else }}
data-default-style-display="none"
{{ end }}
>
{{ if or .Data.RememberedSignInMethod .Data.IDPDiscoveryEnabled }}
<div>Sign in with</div>
{{ end }}
</div>

<div id="idps-list">
{{ range $method := .Data.Methods }}
<button
type="submit"
class="href-card {{ if $.Data.RememberedSignInMethod }}{{ if eq .ID $.Data.RememberedIDP.ID }}visible-flex{{ else }}non-visible{{ end }}{{ else }}{{ if .Hidden }}non-visible{{ else }}visible-flex{{ end }}{{ end }}"
name="authentication_id"
value="{{ .ID }}"
title="{{ .Name }}"
form="sign-in-form"
formaction="?{{ $.Data.Query }}"
{{ if $.Data.RememberedSignInMethod }}
{{ if eq .ID $.Data.RememberedIDP.ID }}
data-default-style-display="flex"
{{ else }}
data-default-style-display="none"
{{ end }}
{{ else }}
{{ if .Hidden }}
data-default-style-display="none"
{{ else }}
data-default-style-display="flex"
{{ end }}
{{ end }}
>
<div>
<img
{{ if and .LogoURI (gt (len .LogoURI) 0) }}
src="{{ .LogoURI }}"
{{ else }}
{{ if eq .Method "github" }}
src="/static/images/idps/github.svg"
{{ else if eq .Method "okta" }}
src="/static/images/idps/okta.svg"
{{ else if eq .Method "auth0" }}
src="/static/images/idps/auth0-icon.svg"
{{ else if eq .Method "saml" }}
src="/static/images/idps/saml-icon.svg"
{{ else if eq .Method "azureb2c" }}
src="/static/images/idps/azure-b2c-icon.svg"
{{ else if eq .Method "azure" }}
src="/static/images/idps/azure-icon.svg"
{{ else if eq .Method "cognito" }}
src="/static/images/idps/cognito-icon.svg"
{{ else if eq .Method "intelli_trust" }}
src="/static/images/idps/entrust-icon.svg"
{{ else if eq .Method "oidc" }}
src="/static/images/idps/openid-icon.svg"
{{ else if eq .Method "custom" }}
src="/static/images/idps/custom-circle.svg"
{{ else if eq .Method "external" }}
src="/static/images/idps/external-icon.svg"
{{ else if eq .Method "identity_pool" }}
src="/static/images/idps/cloudentity-icon.svg"
{{ else if eq .Method "github_embedded" }}
src="/static/images/idps/github.svg"
{{ else if eq .Method "google_embedded" }}
src="/static/images/idps/google-icon.svg"
{{ else if eq .Method "google" }}
src="/static/images/idps/google-icon.svg"
{{ else if eq .Method "static" }}
src="/static/images/idps/sandbox-icon.svg"
{{ else }}
src=""
{{ end }}
{{ end }}
alt="{{ .Method }}"
>
<span>{{ .Name }}</span>
</div>
<i class="material-icons">navigate_next</i>
</button>
{{ end }}
</div>

<div id="remember-my-sign-in-method">
<div class="login-remember-idp-switch-container">
<label class="switch-container" tabindex="0">
<div class="switch">
<input type="checkbox" name="remember_my_sign_in_method" tabindex="-1" {{ if .Data.RememberedSignInMethod }} checked {{ end }}>
<span class="slider"></span>
</div>
<span>Remember me</span>
</label>
</div>
</div>

{{ if .Data.IDPDiscoveryEnabled }}
<input type="hidden" id="idp-discovery-authentication-id" name="authentication_id" value="">
{{ end }}

</form>

{{ end }}
Loading