Skip to content

Commit d3454bb

Browse files
committed
Update Shrine README
1 parent 60c630e commit d3454bb

File tree

1 file changed

+83
-226
lines changed

1 file changed

+83
-226
lines changed

Shrine/README.md

Lines changed: 83 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Credential Manager Sample App
1+
# Shrine Sample App
22

33
This is the repository for the Credential Manager API code integration app,
44
also known as the **"Shrine"** app.
@@ -14,242 +14,99 @@ Credential Manager with your own apps.
1414
This sample app implements the following use cases:
1515

1616
* Create an account using username and set the session using password.
17-
* Generate a new passkey for an existing account
17+
* Generate a new passkey for an existing account.
18+
* Create a passkey-only account using username.
1819
* Store the credentials for created accounts in the user's Google Password Manager account.
19-
* Sign in flow with passkeys support
20-
* Sign in flow with restore credentials support
21-
* Logout from the account.
20+
* Sign in flow with passkeys and password support.
21+
* Sign in flow with Sign in with Google.
22+
* Sign in flow with restore credentials support.
23+
* Passkey management in settings.
24+
* Internal session management.
2225

2326
## Requirements
2427

2528
* Latest release of [Android Studio](https://developer.android.com/studio)
2629
* Java 11 or higher
27-
* A web browser with the ability to access [Glitch](https://glitch.com/).
2830

2931
## Typical account creation and login flow
3032

31-
* Launch the app
32-
* Create an account by sending any username to the server
33-
* Set a session by sending a password in step 2
34-
* Register user credentials using your fingerprint sensor
35-
* From the list of passkeys options shown in the bottom sheet, select the correct passkey option to login
36-
* Logout of the application and close the app
37-
* [Optional] Create multiple accounts and switch accounts to test the implementation
38-
39-
40-
## How to setup your own Glitch.me server
41-
42-
The Shrine app sends requests to a Glitch.me server, and out of the box this code example has been
43-
configured to use a Glitch instance that we've created. To use your own Glitch-hosted backend,
44-
follow these steps. The backend code uses your Android package and SHA fingerprint, and you will
45-
update these on the server.
46-
47-
1. Go to the edit page of the website at [https://glitch.com/edit/#!/credential-manager-app-test](https://glitch.com/edit/#!/credential-manager-app-test)
48-
49-
2. Find the ***"Remix to Edit"*** button at the top right corner. By pressing the button, you can fork the code and continue this tutorial with your own version of the project and services.
50-
51-
3. To use the API on an Android app, you need to associate it with a website and share credentials between them. To set this up, you'll use [Digital Asset Links](https://developer.android.com/training/sign-in/passkeys#add-support-dal). Digital Asset Links files are used to declare associations by hosting a JSON file on your website, and adding a link to this file to your app's manifest. Normally, you'll define an association between your app and the website by creating a JSON file and put it at `.well-known/assetlinks.json` on your HTTPS server. **For this demo, we have a server code that creates an `assetlinks.json` file automatically, just by adding the following environment params to the `.env` file in Glitch:**
52-
53-
1. In the Glitch left nav Files section, click on the `.env` file. This opens up your project's Environment Config. Fill in the following values:
54-
55-
2. `HOSTNAME`: The name of your newly created Glitch service. The project name is found on top left of your Glitch project screen. It'll be something like `peaceful-banana-fern`. Paste or type in the name of your Glitch project into the `HOSTNAME` section.
56-
57-
3. `ANDROID_PACKAGENAME`: The package name of your app, such as `com.google.credentialmanager.sample`. You can find the package name in your project's app-level `build.gradle` file as the value of the `applicationId` property within the `android` block.
58-
59-
4. `ANDROID_SHA256HASH`: SHA-256 hash of your signing certificate. To get the SHA-256 hash of your developer signing certificate, use the following command: `keytool -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore`. The default password of the debug keystore is "android". The SHA256 value appears under Certificate fingerprints. (`75:89:78:74:...`)
60-
61-
62-
4. In your `build.gradle`'s `android` block, find the fields for `buildConfigField` and `resValue`, and update the following values.
63-
64-
1. `buildConfigField / API_BASE_URL`: The URL of your new Glitch server's API. It'll be the full URL + /path appended to the end. For example: `https://peaceful-banana-fern.glitch.me/auth`
65-
66-
2. `resValue / host`: The root URL for your server. For example: [https://peaceful-banana-fern.glitch.me](https://peaceful-banana-fern.glitch.me)
67-
68-
69-
5. Sync your `build.gradle` changes by running **File > Sync Project with Gradle Files**.
70-
71-
6. Test building your app. Run a physical or emulated device that has a valid and passkey-enabled Google account set up, then run your app on it. You should see the Shrine app home screen appear, with Sign In and Sign Up buttons. Don't click anything just yet, you'll do that in the next step.
72-
73-
74-
## Integration
75-
76-
Follow these steps to test Credential Manager integration. In the app, look for a toast to appear to indicate a response success or failure on each step.
77-
78-
### Create an account for username on the server
79-
80-
1. When your app runs the first time, you should see a screen with buttons for Sign In and Sign Up. Click the **Sign Up** button. The **Create Account** screen appears.
81-
82-
2. Enter an email address and unique password and click the **Submit** button.
83-
84-
3. You should now see a **Create a passkey** screen. Click **Create a passkey**.
85-
86-
4. You should see a Google Password Manager bottom sheet appear, offering to save your credentials. Click **Continue**.
87-
88-
5. The Shrine app should then show the **Create a passkey** screen. Click the **Create a passkey** button and a Google Password Manager bottom sheet should appear that offers to create a passkey for your app. Click **Continue**. You should now see the Shrine main menu.
89-
90-
6. Click **Step 1: Send Username** after adding a username and email in the 1st field. For demo purposes, the app and the server will accept any username.
91-
92-
7. Check the username, and create a new account if it doesn't exist.
93-
94-
8. Set a `username` in the session.
95-
96-
9. Wait for the toast to appear saying "Username verified successfully". If you don't see toast, check the logs for errors.
97-
98-
99-
### **Set a session on server**
100-
101-
This step demonstrates if developers want to do additional authentication (2FA). This step shows how 2FA can be done while using passkeys for authentication.
102-
103-
1. Check **Step 2: Send Password**. Above that field, add any password. Type and send the request.
104-
105-
2. Verify the user credential and let the user sign-in. No preceding registration is required.
106-
107-
3. Wait for the toast to appear that says "Session-id stored successfully, Do register!"
108-
109-
4. If you don't see toast, check the logs for errors.
110-
111-
112-
### **Pass required information to a passkey creation prompt**
113-
114-
This section describes how to send a registration request to the server and pass the required information to a passkey creation prompt.
115-
116-
1. Register the passkey credential. Inside `AuthRepository.kt`, find `registerRequest`.
117-
118-
2. Once the request is sent from the client, this method calls the server API `/auth/registerRequest`. The API returns an `ApiResult` with all the `PublicKeyCredentialCreationOptions` that the client needs to generate a new credential.
119-
120-
121-
### **Create a passkey**
122-
123-
In this section, you'll create a passkey with the response received from `/registerRequest`.
124-
125-
1. Parse the params as per needed for the create credentials call. Call `createPasskey()` from `Auth.kt`
126-
127-
2. Give users the choice to enroll a passkey and use it for re-authentication by registering a user credential using a `CreatePublicKeyCredentialRequest()` object.
128-
129-
3. This method calls `createCredential()` from *Credential Manager API*, which registers a user credential that can be used to authenticate the user to the app in the future. This method launches framework UI flows for a user to view their registration options, grant consent, etc.
130-
131-
4. Use your fingerprint or other auth. Methods from your device to register.
132-
133-
134-
### **Send the registration response and register a user credential**
135-
136-
This section describes how to send a registration response back to the server and register a user credential on a server.
137-
138-
- Call `/registerResponse`. This `registerResponse` method is called after the user interface successfully generates a new credential, and you want to send it back to the server.
139-
140-
- Use the response received from the `createPasskey()` call and pass it back to your server.
141-
142-
- Remember the ID of your local key so you can distinguish it from other keys registered on the server. In the `PublicKeyCredential` object, use the `rawId` property.
143-
144-
- The returned value contains a list of all the credentials registered on the server, including the new one.
145-
146-
147-
### **How to retrieve previously stored credentials for user’s account**
148-
149-
You now have a credential registered on the app and the server. You can now use it to let the user sign in.
150-
151-
1. Initiate a server check:
152-
153-
- Open the file `AuthRepository.kt`.
154-
155-
- Examine the `signinRequest` object.
156-
157-
- Send a request to your server to confirm if Credential Manager APIs can be used for user sign-in.
158-
159-
- Provide the `sessionId` and `credentialId` (which were stored locally) as data for this request.
160-
161-
162-
2. Prompt the user for stored credentials:
163-
164-
- If the server request is successful, call the `getPasskey()` function from the `Auth.kt` file to display the user's stored credentials.
165-
166-
3. Configure the retrieval request:
167-
168-
- Create a `GetCredentialRequest()`.
169-
170-
- Provide the previously created registration options to this request.
171-
172-
- For the `isAutoSelectAllowed()` flag:
173-
174-
175-
- Set to `true` if you want a single stored credential to be automatically selected.
176-
177-
- Set to `false` to require manual selection.
178-
179-
180-
4. Retrieve credentials:
181-
182-
- Use `PublicKeyCredentialOptions` with the `GetCredentialRequest` to retrieve all the user's eligible credentials.
183-
184-
- Ensure that the `requestJson` argument is in a valid WebAuthn JSON format.
185-
186-
187-
5. Display the credential selection interface:
188-
189-
190-
- Call `CredentialManager.getCredential()` to display a bottom sheet interface.
191-
192-
- This UI displays a list of previously saved credentials. The user can then select a credential and provide consent to proceed with authentication.
193-
194-
195-
### **Send a sign-in response and authenticate the user**
196-
197-
This section describes how to send a sign-in response to the server and authenticate your user.
198-
199-
1. Call `signinResponse` from `AuthRespository.kt`. Pass the response and credential to the method as parameters.
200-
201-
2. If successful, the user has been signed in and you can redirect them to the home screen.
202-
203-
204-
### **Restore credentials of a returning user on a new device**
205-
206-
This section describes how to implement restore credentials
207-
208-
1. On a successful user authentication, create a Restore Key
209-
210-
1. Call `AuthRepository`'s `registerPasskeyCreationRequest` method
211-
212-
2. With the PasskeyCreationRequest recieved from the above method, call `CredentialManagerUtils`'s `createRestoreKey` method
213-
214-
3. Then call `AuthRepository`'s `registerPasskeyCreationResponse` method
215-
216-
217-
2. Once on a new device, check if there is any restore key present on the device or not (brought to the new device in the process of Backup and Restore)
218-
219-
1. Call `AuthRepository`'s `signInWithPasskeyOrPasswordRequest` method
220-
221-
2. With the PasskeyCreationRequest recieved from the above method, call `CredentialManagerUtils`'s `getRestoreKey` method
222-
223-
3. If there is a RestoreKey present this will return a `GenericCredentialManagerResponse.GetCredentialSuccess` else this will return a `GenericCredentialManagerResponse.Error`
224-
225-
226-
3. Sign in using the found Restore Key
227-
228-
1. If a restore key is found in the above step, simply use it to sign-in using `AuthRepository`'s `signInWithPasskeysResponse` method
229-
230-
231-
4. Delete a Restore Key
232-
233-
1. If a user logs out of the app, make sure to clear the stored restore key by calling `CredentialManagerUtils`'s `deleteRestoreKey`
234-
235-
236-
## **Specific use case handling**
237-
238-
- When there are no passkeys associated with accounts registered, the developer should catch the exception code and let the user know that first he needs to create a passkey before they try to fetch it.
239-
240-
- For Begin Sign In Failure: 16: Caller has been temporarily blocked due to too many canceled sign-in prompt errors: This is a FIDO-specific error.
241-
242-
- For Begin Sign In Failure: 8: Unknown internal error. If the phone isn't set up properly with a Google account, the passkey JSON is being created incorrectly.
243-
244-
- For `publickeycredential.CreatePublicKeyCredentialDomException`: The incoming request cannot be validated: This means your application package ID is not registered with your server. Validate this with your server code.
245-
246-
247-
### **How to build a debug signed version**
248-
249-
To build a debug signed version of this sample app, you need to update the `API_BASE_URL` to `https://credential-manager-app-test.glitch.me/auth` and `host` field to `https://credman-glitch-sample.glitch.me` under `buildConfigField` in your `build.gradle` file.
33+
* Launch the app.
34+
* Create an account with a passkey or password, or directly log in with your Google account
35+
* When creating a passkey, you will need to use your lock screen credentials. This means you will
36+
need to enable a lock screen if you don't have one.
37+
* Once an account is created, you can sign in with the method you chose.
38+
* For passkey login, select the passkey from the list of passkeys options shown in the bottom sheet.
39+
For password login, select the respective password option shown in the bottom sheet.
40+
* Manage your passkeys by going into settings.
41+
* Log out of the application to restart the flow.
42+
* [Optional] Create multiple accounts and switch accounts to test the implementation.
43+
44+
## Design
45+
46+
This section will cover the general components of the Shrine app. The typical app flow would look
47+
like the following:
48+
49+
1. User interacts with a screen, i.e. to create a credential via the "Sign up" button on
50+
`RegisterScreen.kt`.
51+
2. A callback is triggered, usually tied to a View Model function, i.e.
52+
`viewModel.onPasskeyRegister()`.
53+
3. The View Model function calls the appropriate `AuthRepository.kt` function to send a request to
54+
the server, i.e. `repository.registerUsername()` to first register the username with the server.
55+
4. `AuthRepository.kt` contains an injected `AuthApiService` instance which implements Retrofit
56+
calls to the Project Sesame (app) server. Server requests and responses are handled in
57+
`AuthRepository.kt` and then passed to the calling View Model. Internal session states are also
58+
updated as needed.
59+
5. The View Model handles the returned response and makes other server calls as needed, i.e.
60+
`createPasskey()`. In this case, `createPasskey()` also contains a callback with
61+
`credentialManager.createCredential()` once initial server communication succeeds, via a
62+
`CredentialManagerUtils` object that was passed from the Screen.
63+
6. The View Model updates the UI with success or failure, and navigates the user to the next screen
64+
as appropriate.
65+
66+
### Server
67+
68+
This app utilizes the server at https://project-sesame-426206.appspot.com/ which is specified in the
69+
`app/build.gradle.kts` file. This server handles all requests related to passwords and passkeys (
70+
creation, authentication, deletion), as well as Sign in with Google. You can see the implementation
71+
of this server at https://github.com/GoogleChromeLabs/project-sesame. The app uses Retrofit to
72+
handle web communications with the server, with endpoints specified in`api/AuthApiService.kt` and
73+
called in `repository/AuthRepository.kt`.
74+
75+
### Screens
76+
77+
The frontend elements are contained in the various Screens in the `ui` package. Each Screen utilizes
78+
a View Model that generally has the same name, with a few exceptions. These Screens primarily handle
79+
frontend element placements and error message displays.
80+
81+
Some Screens utilize Credential Manager calls (i.e. `RegisterScreen.kt`). As a Credential Manager
82+
object requires an Activity, the corresponding Context is passed in through the Screen to the
83+
`CredentialManagerUtils.kt` file which creates and manages the actual Credential Manager object.
84+
85+
### View Models
86+
87+
The View Models generally manage sending, receiving, and processing responses between the Screens
88+
and app server. Server communication is handled via an injected `AuthRepository` object. Uistate is
89+
also updated in the View Model, which is in turn monitored and updated in the Screen.
90+
91+
### Credential Manager
92+
93+
Credential manager calls are made in `CredentialManagerUtils.kt`. The usage is fairly
94+
straightforward, i.e. `.createCredential()` and `.getCredential()` for creating and retrieving
95+
credentials, respectively. Please note the handling of exceptions, as some may be caused by fairly
96+
common usage, i.e. user cancelling the selector.
97+
98+
### Requests and responses
99+
100+
The requests and responses sent and received from the server are defined in the `model` package.
101+
These consist of various data classes that are defined based on the expected response from the
102+
Project Sesame server, i.e. the data class `RegisterRequestResponse` defines fields expected from
103+
server response from requesting passkey registration options. This is then used in `registerRequest`
104+
in `AuthApiService.kt`, and the server's returned JSON is expected to have those fields. You can
105+
verify this by looking at the logs in Logcat after making any server request.
250106

251107
## **License**
252108

253-
Shrine is distributed under the terms of the Apache License (Version 2.0). See the license for more information.
109+
Shrine is distributed under the terms of the Apache License (Version 2.0). See the license for more
110+
information.
254111

255112
**

0 commit comments

Comments
 (0)