Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Build

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

env:
SOLUTION_PATH: src/Maui.CredentialManagers.slnx
DOTNET_VERSION: '10.0.x'
BUILD_CONFIGURATION: Release

jobs:
build:
runs-on: macos-latest

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install MAUI workload
run: dotnet workload install maui

- name: Restore dependencies
run: dotnet restore ${{ env.SOLUTION_PATH }}

- name: Build
run: dotnet build ${{ env.SOLUTION_PATH }} --no-restore --configuration ${{ env.BUILD_CONFIGURATION }}

- name: Test with coverage
run: dotnet test ${{ env.SOLUTION_PATH }} --no-build --configuration ${{ env.BUILD_CONFIGURATION }} --collect:"XPlat Code Coverage" --results-directory ./coverage

- name: Find coverage file
id: coverage
run: echo "file=$(find ./coverage -name 'coverage.cobertura.xml' | head -1)" >> $GITHUB_OUTPUT

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: ${{ steps.coverage.outputs.file }}
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
Comment on lines +34 to +47

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow runs dotnet test on src/Maui.CredentialManagers.slnx, but that solution currently only contains the library project (no test project / Microsoft.NET.Test.Sdk). dotnet test will fail or produce no coverage in this setup. Either add a proper test project to the solution, or remove the test/coverage steps (and Codecov upload) to keep CI green.

Suggested change
- name: Test with coverage
run: dotnet test ${{ env.SOLUTION_PATH }} --no-build --configuration ${{ env.BUILD_CONFIGURATION }} --collect:"XPlat Code Coverage" --results-directory ./coverage
- name: Find coverage file
id: coverage
run: echo "file=$(find ./coverage -name 'coverage.cobertura.xml' | head -1)" >> $GITHUB_OUTPUT
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: ${{ steps.coverage.outputs.file }}
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}

Copilot uses AI. Check for mistakes.
72 changes: 72 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Publish to NuGet

on:
workflow_dispatch:

env:
PROJECT_PATH: src/Maui.CredentialManagers/Maui.CredentialManagers.csproj
SOLUTION_PATH: src/Maui.CredentialManagers.slnx
PACKAGE_NAME: Kebechet.Maui.CredentialManagers
DOTNET_VERSION: '10.0.x'
BUILD_CONFIGURATION: Release
OUTPUT_PATH: ./nupkg
NUGET_SOURCE: https://api.nuget.org/v3/index.json

jobs:
publish:
runs-on: macos-latest
permissions:
contents: write
packages: write

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install MAUI workload
run: dotnet workload install maui

- name: Get version from csproj
id: version
run: |
VERSION=$(grep -oP '(?<=<Version>)[^<]+' ${{ env.PROJECT_PATH }})

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On macOS runners, the default grep is BSD grep and does not support -P (PCRE). The Get version from csproj step will fail. Consider extracting the version via dotnet msbuild -getProperty:Version, xmllint, or a small PowerShell/Python snippet instead of grep -oP.

Suggested change
VERSION=$(grep -oP '(?<=<Version>)[^<]+' ${{ env.PROJECT_PATH }})
VERSION=$(dotnet msbuild ${{ env.PROJECT_PATH }} -nologo -getProperty:Version | tr -d '[:space:]')

Copilot uses AI. Check for mistakes.
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"

- name: Restore dependencies
run: dotnet restore ${{ env.SOLUTION_PATH }}

- name: Build
run: dotnet build ${{ env.SOLUTION_PATH }} --configuration ${{ env.BUILD_CONFIGURATION }} --no-restore

- name: Pack
run: dotnet pack ${{ env.PROJECT_PATH }} --configuration ${{ env.BUILD_CONFIGURATION }} --no-build --output ${{ env.OUTPUT_PATH }}

- name: Publish to NuGet
run: dotnet nuget push ${{ env.OUTPUT_PATH }}/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source ${{ env.NUGET_SOURCE }} --skip-duplicate

- name: Publish to GitHub Packages
run: dotnet nuget push ${{ env.OUTPUT_PATH }}/*.nupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json --skip-duplicate

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.version.outputs.version }}
name: v${{ steps.version.outputs.version }}
body: |
## ${{ env.PACKAGE_NAME }} v${{ steps.version.outputs.version }}

### Installation
```bash
dotnet add package ${{ env.PACKAGE_NAME }} --version ${{ steps.version.outputs.version }}
```

### Links
- [NuGet Package](https://www.nuget.org/packages/${{ env.PACKAGE_NAME }}/${{ steps.version.outputs.version }})
- [GitHub](https://github.com/Kebechet/Maui.CredentialManagers)
files: ${{ env.OUTPUT_PATH }}/*.nupkg
generate_release_notes: true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -399,3 +399,6 @@ FodyWeavers.xsd

# Visual Studio for Mac
**/.DS_Store

# Claude Code
**/.claude/settings.local.json
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Kebechet
Copyright (c) 2023-2026 Kebechet

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,67 @@
# Maui.CredentialManagers
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/kebechet)

# Maui.CredentialManagers
[![NuGet Version](https://img.shields.io/nuget/v/Kebechet.Maui.CredentialManagers)](https://www.nuget.org/packages/Kebechet.Maui.CredentialManagers/)
[![NuGet Downloads](https://img.shields.io/nuget/dt/Kebechet.Maui.CredentialManagers)](https://www.nuget.org/packages/Kebechet.Maui.CredentialManagers/)
[![Build](https://github.com/Kebechet/Maui.CredentialManagers/actions/workflows/build.yml/badge.svg)](https://github.com/Kebechet/Maui.CredentialManagers/actions/workflows/build.yml)
[![codecov](https://codecov.io/gh/Kebechet/Maui.CredentialManagers/graph/badge.svg)](https://codecov.io/gh/Kebechet/Maui.CredentialManagers)
![Last updated](https://img.shields.io/github/last-commit/Kebechet/Maui.CredentialManagers/main?label=last%20updated)
[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/samuel_sidor.svg?style=social&label=Follow%20samuel_sidor)](https://x.com/samuel_sidor)

Cross-platform .NET MAUI library for unified credential management on Android and iOS.

## Installation

```bash
dotnet add package Kebechet.Maui.CredentialManagers
```

## Usage

### Registration

```csharp
builder.Services.AddCredentialManagerService(options =>
{
options.GoogleServerClientId = "xxx.apps.googleusercontent.com";
options.GoogleIosClientId = "yyy.apps.googleusercontent.com";
options.GoogleIosRedirectUri = "com.myapp:/oauth2redirect";
options.AppleServiceId = "com.myapp.auth";
options.AppleRedirectUri = "https://myserver.com/auth/apple/callback";
options.AppleAndroidCallbackScheme = "com.myapp:/applecallback";

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The usage snippet references option properties that don’t exist in CredentialManagerOptions (e.g., GoogleIosClientId, GoogleIosRedirectUri, AppleAndroidCallbackScheme). The current API uses nested platform options (options.Ios.GoogleClientId, options.Ios.GoogleRedirectUri, options.Android.AppleCallbackScheme, etc.). Update the README snippet to match the actual option names so consumers can copy/paste it successfully.

Suggested change
options.GoogleIosClientId = "yyy.apps.googleusercontent.com";
options.GoogleIosRedirectUri = "com.myapp:/oauth2redirect";
options.AppleServiceId = "com.myapp.auth";
options.AppleRedirectUri = "https://myserver.com/auth/apple/callback";
options.AppleAndroidCallbackScheme = "com.myapp:/applecallback";
options.Ios.GoogleClientId = "yyy.apps.googleusercontent.com";
options.Ios.GoogleRedirectUri = "com.myapp:/oauth2redirect";
options.Apple.ServiceId = "com.myapp.auth";
options.Apple.RedirectUri = "https://myserver.com/auth/apple/callback";
options.Android.AppleCallbackScheme = "com.myapp:/applecallback";

Copilot uses AI. Check for mistakes.
});
```

### Inject and use

```csharp
@inject ICredentialManagerService CredentialManagerService

// Save password
await CredentialManagerService.CreatePasswordCredential(
new PasswordCredentialDto { Id = "user@example.com", Password = "secret" },
cancellationToken);

// Retrieve password
var result = await CredentialManagerService.GetPasswordCredential(cancellationToken);

// SSO (Google on Android, Apple on iOS by default)
var ssoResult = await CredentialManagerService.ContinueWithSso(
SsoProvider.PlatformDefault, cancellationToken);

// Clear credential state
await CredentialManagerService.ClearCredentialState(cancellationToken);
```

## Supported Platforms

| Feature | Android | iOS |
|---------|---------|-----|
| Password credentials | Credential Manager API | Keychain Services |
| Google Sign-In | Native (Credential Manager) | WebAuthenticator |
| Apple Sign-In | WebAuthenticator | Native (ASAuthorization) |
| Clear credential state | ClearCredentialState API | Keychain removal |

## License

[MIT](LICENSE)
6 changes: 6 additions & 0 deletions demo/DemoApp.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Solution>
<Project Path="../src/Maui.CredentialManagers/Maui.CredentialManagers.csproj" />
<Project Path="DemoApp/DemoApp.csproj">
<Deploy />
</Project>
</Solution>
17 changes: 17 additions & 0 deletions demo/DemoApp/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DemoApp"
x:Class="DemoApp.App">
<Application.Resources>
<ResourceDictionary>

<!--
For information about styling .NET MAUI pages
please refer to the documentation:
https://go.microsoft.com/fwlink/?linkid=2282329
-->

</ResourceDictionary>
</Application.Resources>
</Application>
14 changes: 14 additions & 0 deletions demo/DemoApp/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace DemoApp;

public partial class App : Application
{
public App()
{
InitializeComponent();
}

protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(new MainPage()) { Title = "DemoApp" };
}
}
7 changes: 7 additions & 0 deletions demo/DemoApp/Components/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@inherits LayoutComponentBase

<main>
<article class="content px-4">
@Body
</article>
</main>
Loading
Loading